basic otr support
This commit is contained in:
parent
e63109215e
commit
42c4c1789a
|
@ -31,14 +31,15 @@ public final class R {
|
||||||
public static final int ic_action_add_person=0x7f020002;
|
public static final int ic_action_add_person=0x7f020002;
|
||||||
public static final int ic_action_delete=0x7f020003;
|
public static final int ic_action_delete=0x7f020003;
|
||||||
public static final int ic_action_refresh=0x7f020004;
|
public static final int ic_action_refresh=0x7f020004;
|
||||||
public static final int ic_action_send=0x7f020005;
|
public static final int ic_action_secure=0x7f020005;
|
||||||
public static final int ic_action_send_now=0x7f020006;
|
public static final int ic_action_send=0x7f020006;
|
||||||
public static final int ic_action_unsecure=0x7f020007;
|
public static final int ic_action_send_now=0x7f020007;
|
||||||
public static final int ic_launcher=0x7f020008;
|
public static final int ic_action_unsecure=0x7f020008;
|
||||||
public static final int ic_profile=0x7f020009;
|
public static final int ic_launcher=0x7f020009;
|
||||||
public static final int message_border=0x7f02000a;
|
public static final int ic_profile=0x7f02000a;
|
||||||
public static final int notification=0x7f02000b;
|
public static final int message_border=0x7f02000b;
|
||||||
public static final int section_header=0x7f02000c;
|
public static final int notification=0x7f02000c;
|
||||||
|
public static final int section_header=0x7f02000d;
|
||||||
}
|
}
|
||||||
public static final class id {
|
public static final class id {
|
||||||
public static final int account_confirm_password_desc=0x7f0a0019;
|
public static final int account_confirm_password_desc=0x7f0a0019;
|
||||||
|
|
BIN
libs/bcprov-jdk15on-150.jar
Normal file
BIN
libs/bcprov-jdk15on-150.jar
Normal file
Binary file not shown.
BIN
libs/otr4j-0.10.jar
Normal file
BIN
libs/otr4j-0.10.jar
Normal file
Binary file not shown.
BIN
res/drawable-hdpi/ic_action_secure.png
Normal file
BIN
res/drawable-hdpi/ic_action_secure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 394 B |
BIN
res/drawable-mdpi/ic_action_secure.png
Normal file
BIN
res/drawable-mdpi/ic_action_secure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 317 B |
BIN
res/drawable-xhdpi/ic_action_secure.png
Normal file
BIN
res/drawable-xhdpi/ic_action_secure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 510 B |
BIN
res/drawable-xxhdpi/ic_action_secure.png
Normal file
BIN
res/drawable-xxhdpi/ic_action_secure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 624 B |
229
src/de/gultsch/chat/crypto/OtrEngine.java
Normal file
229
src/de/gultsch/chat/crypto/OtrEngine.java
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
package de.gultsch.chat.crypto;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
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.DSAPrivateKeySpec;
|
||||||
|
import java.security.spec.DSAPublicKeySpec;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import de.gultsch.chat.entities.Account;
|
||||||
|
import de.gultsch.chat.persistance.DatabaseBackend;
|
||||||
|
import de.gultsch.chat.xml.Element;
|
||||||
|
import de.gultsch.chat.xmpp.MessagePacket;
|
||||||
|
|
||||||
|
import net.java.otr4j.OtrEngineHost;
|
||||||
|
import net.java.otr4j.OtrException;
|
||||||
|
import net.java.otr4j.OtrPolicy;
|
||||||
|
import net.java.otr4j.OtrPolicyImpl;
|
||||||
|
import net.java.otr4j.session.InstanceTag;
|
||||||
|
import net.java.otr4j.session.SessionID;
|
||||||
|
|
||||||
|
public class OtrEngine implements OtrEngineHost {
|
||||||
|
|
||||||
|
private static final String LOGTAG = "xmppService";
|
||||||
|
|
||||||
|
private Account account;
|
||||||
|
private OtrPolicy otrPolicy;
|
||||||
|
private KeyPair keyPair;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public OtrEngine(Context context, Account account) {
|
||||||
|
this.account = account;
|
||||||
|
this.otrPolicy = new OtrPolicyImpl();
|
||||||
|
this.otrPolicy.setAllowV1(false);
|
||||||
|
this.otrPolicy.setAllowV2(true);
|
||||||
|
this.otrPolicy.setAllowV3(true);
|
||||||
|
this.keyPair = loadKey(account.getKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyPair loadKey(JSONObject keys) {
|
||||||
|
if (keys == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
BigInteger x = new BigInteger(keys.getString("otr_x"),16);
|
||||||
|
BigInteger y = new BigInteger(keys.getString("otr_y"),16);
|
||||||
|
BigInteger p = new BigInteger(keys.getString("otr_p"),16);
|
||||||
|
BigInteger q = new BigInteger(keys.getString("otr_q"),16);
|
||||||
|
BigInteger g = new BigInteger(keys.getString("otr_g"),16);
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||||
|
DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g);
|
||||||
|
DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g);
|
||||||
|
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
|
||||||
|
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
|
||||||
|
return new KeyPair(publicKey, privateKey);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveKey() {
|
||||||
|
PublicKey publicKey = keyPair.getPublic();
|
||||||
|
PrivateKey privateKey = keyPair.getPrivate();
|
||||||
|
KeyFactory keyFactory;
|
||||||
|
try {
|
||||||
|
keyFactory = KeyFactory.getInstance("DSA");
|
||||||
|
DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(privateKey, DSAPrivateKeySpec.class);
|
||||||
|
DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, DSAPublicKeySpec.class);
|
||||||
|
this.account.setKey("otr_x",privateKeySpec.getX().toString(16));
|
||||||
|
this.account.setKey("otr_g",privateKeySpec.getG().toString(16));
|
||||||
|
this.account.setKey("otr_p",privateKeySpec.getP().toString(16));
|
||||||
|
this.account.setKey("otr_q",privateKeySpec.getQ().toString(16));
|
||||||
|
this.account.setKey("otr_y",publicKeySpec.getY().toString(16));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishedSessionMessage(SessionID arg0, String arg1)
|
||||||
|
throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFallbackMessage(SessionID arg0) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getLocalFingerprintRaw(SessionID arg0) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException {
|
||||||
|
if (this.keyPair==null) {
|
||||||
|
KeyPairGenerator kg;
|
||||||
|
try {
|
||||||
|
kg = KeyPairGenerator.getInstance("DSA");
|
||||||
|
this.keyPair = kg.genKeyPair();
|
||||||
|
this.saveKey();
|
||||||
|
DatabaseBackend.getInstance(context).updateAccount(account);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.d(LOGTAG,"error generating key pair "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReplyForUnreadableMessage(SessionID arg0) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OtrPolicy getSessionPolicy(SessionID arg0) {
|
||||||
|
return otrPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectMessage(SessionID session, String body) throws OtrException {
|
||||||
|
MessagePacket packet = new MessagePacket();
|
||||||
|
packet.setFrom(account.getFullJid()); //sender
|
||||||
|
packet.setTo(session.getAccountID()+"/"+session.getUserID()); //reciepient
|
||||||
|
packet.setBody(body);
|
||||||
|
Element privateTag = new Element("private");
|
||||||
|
privateTag.setAttribute("xmlns","urn:xmpp:carbons:2");
|
||||||
|
packet.addChild(privateTag);
|
||||||
|
account.getXmppConnection().sendMessagePacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageFromAnotherInstanceReceived(SessionID arg0) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void multipleInstancesDetected(SessionID arg0) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requireEncryptedMessage(SessionID arg0, String arg1)
|
||||||
|
throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showError(SessionID arg0, String arg1) throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void smpAborted(SessionID arg0) throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void smpError(SessionID arg0, int arg1, boolean arg2)
|
||||||
|
throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unencryptedMessageReceived(SessionID arg0, String arg1)
|
||||||
|
throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unreadableMessageReceived(SessionID arg0) throws OtrException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unverify(SessionID arg0, String arg1) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(SessionID arg0, String arg1, boolean arg2) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,14 @@
|
||||||
package de.gultsch.chat.entities;
|
package de.gultsch.chat.entities;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import de.gultsch.chat.crypto.OtrEngine;
|
||||||
|
import de.gultsch.chat.xmpp.XmppConnection;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.util.JsonReader;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class Account extends AbstractEntity{
|
public class Account extends AbstractEntity{
|
||||||
|
@ -15,6 +22,7 @@ public class Account extends AbstractEntity{
|
||||||
public static final String PASSWORD = "password";
|
public static final String PASSWORD = "password";
|
||||||
public static final String OPTIONS = "options";
|
public static final String OPTIONS = "options";
|
||||||
public static final String ROSTERVERSION = "rosterversion";
|
public static final String ROSTERVERSION = "rosterversion";
|
||||||
|
public static final String KEYS = "keys";
|
||||||
|
|
||||||
public static final int OPTION_USETLS = 0;
|
public static final int OPTION_USETLS = 0;
|
||||||
public static final int OPTION_DISABLED = 1;
|
public static final int OPTION_DISABLED = 1;
|
||||||
|
@ -34,23 +42,32 @@ public class Account extends AbstractEntity{
|
||||||
protected String rosterVersion;
|
protected String rosterVersion;
|
||||||
protected String resource;
|
protected String resource;
|
||||||
protected int status = 0;
|
protected int status = 0;
|
||||||
|
protected JSONObject keys = new JSONObject();
|
||||||
|
|
||||||
protected boolean online = false;
|
protected boolean online = false;
|
||||||
|
|
||||||
|
transient OtrEngine otrEngine = null;
|
||||||
|
transient XmppConnection xmppConnection = null;
|
||||||
|
|
||||||
public Account() {
|
public Account() {
|
||||||
this.uuid = "0";
|
this.uuid = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account(String username, String server, String password) {
|
public Account(String username, String server, String password) {
|
||||||
this(java.util.UUID.randomUUID().toString(),username,server,password,0,null);
|
this(java.util.UUID.randomUUID().toString(),username,server,password,0,null,"");
|
||||||
}
|
}
|
||||||
public Account(String uuid, String username, String server,String password, int options, String rosterVersion) {
|
public Account(String uuid, String username, String server,String password, int options, String rosterVersion, String keys) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.rosterVersion = rosterVersion;
|
this.rosterVersion = rosterVersion;
|
||||||
|
try {
|
||||||
|
this.keys = new JSONObject(keys);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOptionSet(int option) {
|
public boolean isOptionSet(int option) {
|
||||||
|
@ -109,6 +126,14 @@ public class Account extends AbstractEntity{
|
||||||
return username+"@"+server;
|
return username+"@"+server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JSONObject getKeys() {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String keyName, String keyValue) throws JSONException {
|
||||||
|
this.keys.put(keyName, keyValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
@ -117,6 +142,8 @@ public class Account extends AbstractEntity{
|
||||||
values.put(SERVER, server);
|
values.put(SERVER, server);
|
||||||
values.put(PASSWORD, password);
|
values.put(PASSWORD, password);
|
||||||
values.put(OPTIONS,options);
|
values.put(OPTIONS,options);
|
||||||
|
values.put(KEYS,this.keys.toString());
|
||||||
|
values.put(ROSTERVERSION,rosterVersion);
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,8 +153,28 @@ public class Account extends AbstractEntity{
|
||||||
cursor.getString(cursor.getColumnIndex(SERVER)),
|
cursor.getString(cursor.getColumnIndex(SERVER)),
|
||||||
cursor.getString(cursor.getColumnIndex(PASSWORD)),
|
cursor.getString(cursor.getColumnIndex(PASSWORD)),
|
||||||
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
|
cursor.getInt(cursor.getColumnIndex(OPTIONS)),
|
||||||
cursor.getString(cursor.getColumnIndex(ROSTERVERSION))
|
cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
|
||||||
|
cursor.getString(cursor.getColumnIndex(KEYS))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public OtrEngine getOtrEngine(Context context) {
|
||||||
|
if (otrEngine==null) {
|
||||||
|
otrEngine = new OtrEngine(context,this);
|
||||||
|
}
|
||||||
|
return this.otrEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmppConnection getXmppConnection() {
|
||||||
|
return this.xmppConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setXmppConnection(XmppConnection connection) {
|
||||||
|
this.xmppConnection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullJid() {
|
||||||
|
return this.getJid()+"/"+this.resource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,16 @@ package de.gultsch.chat.entities;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.gultsch.chat.crypto.OtrEngine;
|
||||||
|
import de.gultsch.chat.xmpp.XmppConnection;
|
||||||
|
|
||||||
|
import net.java.otr4j.OtrException;
|
||||||
|
import net.java.otr4j.session.SessionID;
|
||||||
|
import net.java.otr4j.session.SessionImpl;
|
||||||
|
import net.java.otr4j.session.SessionStatus;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -41,6 +50,9 @@ public class Conversation extends AbstractEntity {
|
||||||
private transient Account account = null;
|
private transient Account account = null;
|
||||||
private transient Contact contact;
|
private transient Contact contact;
|
||||||
|
|
||||||
|
private transient SessionImpl otrSession;
|
||||||
|
private transient String foreignOtrPresence;
|
||||||
|
|
||||||
public Conversation(String name, Account account,
|
public Conversation(String name, Account account,
|
||||||
String contactJid, int mode) {
|
String contactJid, int mode) {
|
||||||
this(java.util.UUID.randomUUID().toString(), name, null, account.getUuid(), contactJid, System
|
this(java.util.UUID.randomUUID().toString(), name, null, account.getUuid(), contactJid, System
|
||||||
|
@ -85,19 +97,13 @@ public class Conversation extends AbstractEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLatestMessage() {
|
public Message getLatestMessage() {
|
||||||
if ((this.messages == null)||(this.messages.size()==0)) {
|
if ((this.messages == null)||(this.messages.size()==0)) {
|
||||||
return null;
|
Message message = new Message(this,"",Message.ENCRYPTION_NONE);
|
||||||
|
message.setTime(0);
|
||||||
|
return message;
|
||||||
} else {
|
} else {
|
||||||
return this.messages.get(this.messages.size() - 1).getBody();
|
return this.messages.get(this.messages.size() - 1);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLatestMessageDate() {
|
|
||||||
if ((this.messages == null)||(this.messages.size()==0)) {
|
|
||||||
return this.getCreated();
|
|
||||||
} else {
|
|
||||||
return this.messages.get(this.messages.size() - 1).getTimeSent();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,4 +204,30 @@ public class Conversation extends AbstractEntity {
|
||||||
public void setMode(int mode) {
|
public void setMode(int mode) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void startOtrSession(Context context, String presence) {
|
||||||
|
Log.d("xmppService","starting otr session with "+presence);
|
||||||
|
SessionID sessionId = new SessionID(this.getContactJid(),presence,"xmpp");
|
||||||
|
this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionImpl getOtrSession() {
|
||||||
|
return this.otrSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetOtrSession() {
|
||||||
|
this.otrSession = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endOtrIfNeeded() throws OtrException {
|
||||||
|
if (this.otrSession!=null) {
|
||||||
|
if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
|
||||||
|
this.otrSession.endSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOtrSession() {
|
||||||
|
return (this.otrSession!=null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
|
+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
|
||||||
+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
|
+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
|
||||||
+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
|
+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
|
||||||
+ " NUMBER)");
|
+ " NUMBER, "+Account.KEYS+" TEXT)");
|
||||||
db.execSQL("create table " + Conversation.TABLENAME + " ("
|
db.execSQL("create table " + Conversation.TABLENAME + " ("
|
||||||
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
|
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
|
||||||
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
|
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
|
||||||
|
|
|
@ -6,6 +6,12 @@ import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.java.otr4j.OtrException;
|
||||||
|
import net.java.otr4j.session.Session;
|
||||||
|
import net.java.otr4j.session.SessionImpl;
|
||||||
|
import net.java.otr4j.session.SessionStatus;
|
||||||
|
|
||||||
import de.gultsch.chat.entities.Account;
|
import de.gultsch.chat.entities.Account;
|
||||||
import de.gultsch.chat.entities.Contact;
|
import de.gultsch.chat.entities.Contact;
|
||||||
|
@ -50,8 +56,6 @@ public class XmppConnectionService extends Service {
|
||||||
private List<Account> accounts;
|
private List<Account> accounts;
|
||||||
private List<Conversation> conversations = null;
|
private List<Conversation> conversations = null;
|
||||||
|
|
||||||
private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>();
|
|
||||||
|
|
||||||
private OnConversationListChangedListener convChangedListener = null;
|
private OnConversationListChangedListener convChangedListener = null;
|
||||||
private OnAccountListChangedListener accountChangedListener = null;
|
private OnAccountListChangedListener accountChangedListener = null;
|
||||||
|
|
||||||
|
@ -73,7 +77,9 @@ public class XmppConnectionService extends Service {
|
||||||
if ((packet.getType() == MessagePacket.TYPE_CHAT)
|
if ((packet.getType() == MessagePacket.TYPE_CHAT)
|
||||||
|| (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) {
|
|| (packet.getType() == MessagePacket.TYPE_GROUPCHAT)) {
|
||||||
boolean notify = true;
|
boolean notify = true;
|
||||||
|
boolean runOtrCheck = false;
|
||||||
int status = Message.STATUS_RECIEVED;
|
int status = Message.STATUS_RECIEVED;
|
||||||
|
int encryption = Message.ENCRYPTION_NONE;
|
||||||
String body;
|
String body;
|
||||||
String fullJid;
|
String fullJid;
|
||||||
if (!packet.hasChild("body")) {
|
if (!packet.hasChild("body")) {
|
||||||
|
@ -106,6 +112,7 @@ public class XmppConnectionService extends Service {
|
||||||
} else {
|
} else {
|
||||||
fullJid = packet.getFrom();
|
fullJid = packet.getFrom();
|
||||||
body = packet.getBody();
|
body = packet.getBody();
|
||||||
|
runOtrCheck = true;
|
||||||
}
|
}
|
||||||
Conversation conversation = null;
|
Conversation conversation = null;
|
||||||
String[] fromParts = fullJid.split("/");
|
String[] fromParts = fullJid.split("/");
|
||||||
|
@ -124,9 +131,51 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
counterPart = fullJid;
|
counterPart = fullJid;
|
||||||
|
if ((runOtrCheck) && body.startsWith("?OTR")) {
|
||||||
|
if (!conversation.hasOtrSession()) {
|
||||||
|
conversation.startOtrSession(
|
||||||
|
getApplicationContext(), fromParts[1]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Session otrSession = conversation.getOtrSession();
|
||||||
|
SessionStatus before = otrSession
|
||||||
|
.getSessionStatus();
|
||||||
|
body = otrSession.transformReceiving(body);
|
||||||
|
SessionStatus after = otrSession.getSessionStatus();
|
||||||
|
if ((before != after)
|
||||||
|
&& (after == SessionStatus.ENCRYPTED)) {
|
||||||
|
Log.d(LOGTAG, "otr session etablished");
|
||||||
|
List<Message> messages = conversation
|
||||||
|
.getMessages();
|
||||||
|
for (int i = 0; i < messages.size(); ++i) {
|
||||||
|
Message msg = messages.get(i);
|
||||||
|
if ((msg.getStatus() == Message.STATUS_UNSEND)
|
||||||
|
&& (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
|
||||||
|
MessagePacket outPacket = prepareMessagePacket(
|
||||||
|
account, msg, otrSession);
|
||||||
|
msg.setStatus(Message.STATUS_SEND);
|
||||||
|
databaseBackend.updateMessage(msg);
|
||||||
|
account.getXmppConnection()
|
||||||
|
.sendMessagePacket(outPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (convChangedListener!=null) {
|
||||||
|
convChangedListener.onConversationListChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d(LOGTAG, "error receiving otr. resetting");
|
||||||
|
conversation.resetOtrSession();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (body == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
encryption = Message.ENCRYPTION_OTR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Message message = new Message(conversation, counterPart, body,
|
Message message = new Message(conversation, counterPart, body,
|
||||||
Message.ENCRYPTION_NONE, status);
|
encryption, status);
|
||||||
if (packet.hasChild("delay")) {
|
if (packet.hasChild("delay")) {
|
||||||
try {
|
try {
|
||||||
String stamp = packet.findChild("delay").getAttribute(
|
String stamp = packet.findChild("delay").getAttribute(
|
||||||
|
@ -169,14 +218,14 @@ public class XmppConnectionService extends Service {
|
||||||
databaseBackend.clearPresences(account);
|
databaseBackend.clearPresences(account);
|
||||||
connectMultiModeConversations(account);
|
connectMultiModeConversations(account);
|
||||||
List<Conversation> conversations = getConversations();
|
List<Conversation> conversations = getConversations();
|
||||||
for(int i = 0; i < conversations.size(); ++i) {
|
for (int i = 0; i < conversations.size(); ++i) {
|
||||||
if (conversations.get(i).getAccount()==account) {
|
if (conversations.get(i).getAccount() == account) {
|
||||||
sendUnsendMessages(conversations.get(i));
|
sendUnsendMessages(conversations.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (convChangedListener!=null) {
|
if (convChangedListener != null) {
|
||||||
convChangedListener.onConversationListChanged();
|
convChangedListener.onConversationListChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -216,9 +265,20 @@ public class XmppConnectionService extends Service {
|
||||||
databaseBackend.updateContact(contact);
|
databaseBackend.updateContact(contact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
replaceContactInConversation(contact);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void replaceContactInConversation(Contact contact) {
|
||||||
|
List<Conversation> conversations = getConversations();
|
||||||
|
for(int i = 0; i < conversations.size(); ++i) {
|
||||||
|
if (conversations.get(i).getContact().equals(contact)) {
|
||||||
|
conversations.get(i).setContact(contact);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class XmppConnectionBinder extends Binder {
|
public class XmppConnectionBinder extends Binder {
|
||||||
public XmppConnectionService getService() {
|
public XmppConnectionService getService() {
|
||||||
return XmppConnectionService.this;
|
return XmppConnectionService.this;
|
||||||
|
@ -228,13 +288,9 @@ public class XmppConnectionService extends Service {
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
for (Account account : accounts) {
|
for (Account account : accounts) {
|
||||||
if (!connections.containsKey(account)) {
|
if (account.getXmppConnection() == null) {
|
||||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||||
this.connections.put(account,
|
account.setXmppConnection(this.createConnection(account));
|
||||||
this.createConnection(account));
|
|
||||||
} else {
|
|
||||||
Log.d(LOGTAG, account.getJid()
|
|
||||||
+ ": not starting because it's disabled");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,6 +306,16 @@ public class XmppConnectionService extends Service {
|
||||||
ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
|
ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
for (Account account : accounts) {
|
||||||
|
if (account.getXmppConnection() != null) {
|
||||||
|
disconnect(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public XmppConnection createConnection(Account account) {
|
public XmppConnection createConnection(Account account) {
|
||||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
XmppConnection connection = new XmppConnection(account, pm);
|
XmppConnection connection = new XmppConnection(account, pm);
|
||||||
|
@ -261,25 +327,91 @@ public class XmppConnectionService extends Service {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessage(Account account, Message message) {
|
private void startOtrSession(Conversation conv) {
|
||||||
|
Set<String> presences = conv.getContact().getPresences()
|
||||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
.keySet();
|
||||||
MessagePacket packet = prepareMessagePacket(account, message);
|
if (presences.size() == 0) {
|
||||||
connections.get(account).sendMessagePacket(packet);
|
Log.d(LOGTAG, "counter part isnt online. cant use otr");
|
||||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
return;
|
||||||
message.setStatus(Message.STATUS_SEND);
|
} else if (presences.size() == 1) {
|
||||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
conv.startOtrSession(getApplicationContext(),
|
||||||
databaseBackend.createMessage(message);
|
(String) presences.toArray()[0]);
|
||||||
message.getConversation().getMessages().add(message);
|
try {
|
||||||
if (convChangedListener!=null) {
|
conv.getOtrSession().startSession();
|
||||||
convChangedListener.onConversationListChanged();
|
} catch (OtrException e) {
|
||||||
|
Log.d(LOGTAG, "couldnt actually start");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String latestCounterpartPresence = null;
|
||||||
|
List<Message> messages = conv.getMessages();
|
||||||
|
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||||
|
if (messages.get(i).getStatus() == Message.STATUS_RECIEVED) {
|
||||||
|
String[] parts = messages.get(i).getCounterpart()
|
||||||
|
.split("/");
|
||||||
|
if (parts.length == 2) {
|
||||||
|
latestCounterpartPresence = parts[1];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (presences.contains(latestCounterpartPresence)) {
|
||||||
|
conv.startOtrSession(getApplicationContext(),
|
||||||
|
latestCounterpartPresence);
|
||||||
|
try {
|
||||||
|
conv.getOtrSession().startSession();
|
||||||
|
} catch (OtrException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
Log.d(LOGTAG, "couldnt actually start");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(LOGTAG,
|
||||||
|
"could not decide where to send otr connection to");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(Account account, Message message) {
|
||||||
|
Conversation conv = message.getConversation();
|
||||||
|
boolean saveInDb = false;
|
||||||
|
boolean addToConversation = false;
|
||||||
|
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||||
|
MessagePacket packet;
|
||||||
|
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
||||||
|
if (!conv.hasOtrSession()) {
|
||||||
|
//starting otr session. messages will be send later
|
||||||
|
startOtrSession(conv);
|
||||||
|
} else {
|
||||||
|
//otr session aleary exists, creating message packet accordingly
|
||||||
|
packet = prepareMessagePacket(account, message,
|
||||||
|
conv.getOtrSession());
|
||||||
|
account.getXmppConnection().sendMessagePacket(packet);
|
||||||
|
message.setStatus(Message.STATUS_SEND);
|
||||||
|
}
|
||||||
|
saveInDb = true;
|
||||||
|
addToConversation = true;
|
||||||
|
} else {
|
||||||
|
// don't encrypt
|
||||||
|
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
||||||
|
message.setStatus(Message.STATUS_SEND);
|
||||||
|
saveInDb = true;
|
||||||
|
addToConversation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet = prepareMessagePacket(account, message, null);
|
||||||
|
account.getXmppConnection().sendMessagePacket(packet);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
message.getConversation().getMessages().add(message);
|
// account is offline
|
||||||
|
saveInDb = true;
|
||||||
|
addToConversation = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (saveInDb) {
|
||||||
databaseBackend.createMessage(message);
|
databaseBackend.createMessage(message);
|
||||||
if (convChangedListener!=null) {
|
}
|
||||||
|
if (addToConversation) {
|
||||||
|
conv.getMessages().add(message);
|
||||||
|
if (convChangedListener != null) {
|
||||||
convChangedListener.onConversationListChanged();
|
convChangedListener.onConversationListChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,12 +421,11 @@ public class XmppConnectionService extends Service {
|
||||||
private void sendUnsendMessages(Conversation conversation) {
|
private void sendUnsendMessages(Conversation conversation) {
|
||||||
for (int i = 0; i < conversation.getMessages().size(); ++i) {
|
for (int i = 0; i < conversation.getMessages().size(); ++i) {
|
||||||
if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) {
|
if (conversation.getMessages().get(i).getStatus() == Message.STATUS_UNSEND) {
|
||||||
Message message = conversation.getMessages()
|
Message message = conversation.getMessages().get(i);
|
||||||
.get(i);
|
|
||||||
MessagePacket packet = prepareMessagePacket(
|
MessagePacket packet = prepareMessagePacket(
|
||||||
conversation.getAccount(),message);
|
conversation.getAccount(), message, null);
|
||||||
connections.get(conversation.getAccount()).sendMessagePacket(
|
conversation.getAccount().getXmppConnection()
|
||||||
packet);
|
.sendMessagePacket(packet);
|
||||||
message.setStatus(Message.STATUS_SEND);
|
message.setStatus(Message.STATUS_SEND);
|
||||||
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
||||||
databaseBackend.updateMessage(message);
|
databaseBackend.updateMessage(message);
|
||||||
|
@ -307,16 +438,37 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessagePacket prepareMessagePacket(Account account, Message message) {
|
private MessagePacket prepareMessagePacket(Account account,
|
||||||
|
Message message, Session otrSession) {
|
||||||
MessagePacket packet = new MessagePacket();
|
MessagePacket packet = new MessagePacket();
|
||||||
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
if (message.getConversation().getMode() == Conversation.MODE_SINGLE) {
|
||||||
packet.setType(MessagePacket.TYPE_CHAT);
|
packet.setType(MessagePacket.TYPE_CHAT);
|
||||||
|
if (otrSession != null) {
|
||||||
|
try {
|
||||||
|
packet.setBody(otrSession.transformSending(message
|
||||||
|
.getBody()));
|
||||||
|
} catch (OtrException e) {
|
||||||
|
Log.d(LOGTAG,
|
||||||
|
account.getJid()
|
||||||
|
+ ": could not encrypt message to "
|
||||||
|
+ message.getCounterpart());
|
||||||
|
}
|
||||||
|
Element privateMarker = new Element("private");
|
||||||
|
privateMarker.setAttribute("xmlns", "urn:xmpp:carbons:2");
|
||||||
|
packet.addChild(privateMarker);
|
||||||
|
packet.setTo(otrSession.getSessionID().getAccountID()+"/"+otrSession.getSessionID().getUserID());
|
||||||
|
packet.setFrom(account.getFullJid());
|
||||||
|
} else {
|
||||||
|
packet.setBody(message.getBody());
|
||||||
|
packet.setTo(message.getCounterpart());
|
||||||
|
packet.setFrom(account.getJid());
|
||||||
|
}
|
||||||
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
} else if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||||
packet.setType(MessagePacket.TYPE_GROUPCHAT);
|
packet.setType(MessagePacket.TYPE_GROUPCHAT);
|
||||||
|
packet.setBody(message.getBody());
|
||||||
|
packet.setTo(message.getCounterpart());
|
||||||
|
packet.setFrom(account.getJid());
|
||||||
}
|
}
|
||||||
packet.setTo(message.getCounterpart());
|
|
||||||
packet.setFrom(account.getJid());
|
|
||||||
packet.setBody(message.getBody());
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +497,7 @@ public class XmppConnectionService extends Service {
|
||||||
query.setAttribute("xmlns", "jabber:iq:roster");
|
query.setAttribute("xmlns", "jabber:iq:roster");
|
||||||
query.setAttribute("ver", "");
|
query.setAttribute("ver", "");
|
||||||
iqPacket.addChild(query);
|
iqPacket.addChild(query);
|
||||||
connections.get(account).sendIqPacket(iqPacket,
|
account.getXmppConnection().sendIqPacket(iqPacket,
|
||||||
new OnIqPacketReceived() {
|
new OnIqPacketReceived() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -488,7 +640,7 @@ public class XmppConnectionService extends Service {
|
||||||
if (muc) {
|
if (muc) {
|
||||||
conversation.setMode(Conversation.MODE_MULTI);
|
conversation.setMode(Conversation.MODE_MULTI);
|
||||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||||
joinMuc(account, conversation);
|
joinMuc(conversation);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conversation.setMode(Conversation.MODE_SINGLE);
|
conversation.setMode(Conversation.MODE_SINGLE);
|
||||||
|
@ -506,7 +658,7 @@ public class XmppConnectionService extends Service {
|
||||||
conversation = new Conversation(conversationName, account, jid,
|
conversation = new Conversation(conversationName, account, jid,
|
||||||
Conversation.MODE_MULTI);
|
Conversation.MODE_MULTI);
|
||||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||||
joinMuc(account, conversation);
|
joinMuc(conversation);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
conversation = new Conversation(conversationName, account, jid,
|
conversation = new Conversation(conversationName, account, jid,
|
||||||
|
@ -523,6 +675,17 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void archiveConversation(Conversation conversation) {
|
public void archiveConversation(Conversation conversation) {
|
||||||
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
|
leaveMuc(conversation);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
conversation.endOtrIfNeeded();
|
||||||
|
} catch (OtrException e) {
|
||||||
|
Log.d(LOGTAG,
|
||||||
|
"error ending otr session for "
|
||||||
|
+ conversation.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
this.databaseBackend.updateConversation(conversation);
|
this.databaseBackend.updateConversation(conversation);
|
||||||
this.conversations.remove(conversation);
|
this.conversations.remove(conversation);
|
||||||
if (this.convChangedListener != null) {
|
if (this.convChangedListener != null) {
|
||||||
|
@ -537,23 +700,18 @@ public class XmppConnectionService extends Service {
|
||||||
public void createAccount(Account account) {
|
public void createAccount(Account account) {
|
||||||
databaseBackend.createAccount(account);
|
databaseBackend.createAccount(account);
|
||||||
this.accounts.add(account);
|
this.accounts.add(account);
|
||||||
this.connections.put(account, this.createConnection(account));
|
account.setXmppConnection(this.createConnection(account));
|
||||||
if (accountChangedListener != null)
|
if (accountChangedListener != null)
|
||||||
accountChangedListener.onAccountListChangedListener();
|
accountChangedListener.onAccountListChangedListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAccount(Account account) {
|
public void updateAccount(Account account) {
|
||||||
databaseBackend.updateAccount(account);
|
databaseBackend.updateAccount(account);
|
||||||
XmppConnection connection = this.connections.get(account);
|
if (account.getXmppConnection() != null) {
|
||||||
if (connection != null) {
|
disconnect(account);
|
||||||
connection.disconnect();
|
|
||||||
this.connections.remove(account);
|
|
||||||
}
|
}
|
||||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||||
this.connections.put(account, this.createConnection(account));
|
account.setXmppConnection(this.createConnection(account));
|
||||||
} else {
|
|
||||||
Log.d(LOGTAG, account.getJid()
|
|
||||||
+ ": not starting because it's disabled");
|
|
||||||
}
|
}
|
||||||
if (accountChangedListener != null)
|
if (accountChangedListener != null)
|
||||||
accountChangedListener.onAccountListChangedListener();
|
accountChangedListener.onAccountListChangedListener();
|
||||||
|
@ -561,10 +719,8 @@ public class XmppConnectionService extends Service {
|
||||||
|
|
||||||
public void deleteAccount(Account account) {
|
public void deleteAccount(Account account) {
|
||||||
Log.d(LOGTAG, "called delete account");
|
Log.d(LOGTAG, "called delete account");
|
||||||
if (this.connections.containsKey(account)) {
|
if (account.getXmppConnection() != null) {
|
||||||
Log.d(LOGTAG, "found connection. disconnecting");
|
this.disconnect(account);
|
||||||
this.connections.get(account).disconnect();
|
|
||||||
this.connections.remove(account);
|
|
||||||
}
|
}
|
||||||
databaseBackend.deleteAccount(account);
|
databaseBackend.deleteAccount(account);
|
||||||
this.accounts.remove(account);
|
this.accounts.remove(account);
|
||||||
|
@ -596,33 +752,56 @@ public class XmppConnectionService extends Service {
|
||||||
Conversation conversation = conversations.get(i);
|
Conversation conversation = conversations.get(i);
|
||||||
if ((conversation.getMode() == Conversation.MODE_MULTI)
|
if ((conversation.getMode() == Conversation.MODE_MULTI)
|
||||||
&& (conversation.getAccount() == account)) {
|
&& (conversation.getAccount() == account)) {
|
||||||
joinMuc(account, conversation);
|
joinMuc(conversation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void joinMuc(Account account, Conversation conversation) {
|
public void joinMuc(Conversation conversation) {
|
||||||
String muc = conversation.getContactJid();
|
String muc = conversation.getContactJid();
|
||||||
PresencePacket packet = new PresencePacket();
|
PresencePacket packet = new PresencePacket();
|
||||||
packet.setAttribute("to", muc + "/" + account.getUsername());
|
packet.setAttribute("to", muc + "/"
|
||||||
|
+ conversation.getAccount().getUsername());
|
||||||
Element x = new Element("x");
|
Element x = new Element("x");
|
||||||
x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
|
x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
|
||||||
if (conversation.getMessages().size() != 0) {
|
if (conversation.getMessages().size() != 0) {
|
||||||
Element history = new Element("history");
|
Element history = new Element("history");
|
||||||
history.setAttribute(
|
history.setAttribute("seconds",
|
||||||
"seconds",
|
|
||||||
(System.currentTimeMillis() - conversation
|
(System.currentTimeMillis() - conversation
|
||||||
.getLatestMessageDate()) / 1000 + "");
|
.getLatestMessage().getTimeSent() / 1000) + "");
|
||||||
x.addChild(history);
|
x.addChild(history);
|
||||||
}
|
}
|
||||||
packet.addChild(x);
|
packet.addChild(x);
|
||||||
connections.get(conversation.getAccount()).sendPresencePacket(packet);
|
conversation.getAccount().getXmppConnection()
|
||||||
|
.sendPresencePacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnectMultiModeConversations() {
|
public void leaveMuc(Conversation conversation) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disconnect(Account account) {
|
||||||
|
List<Conversation> conversations = getConversations();
|
||||||
|
for (int i = 0; i < conversations.size(); i++) {
|
||||||
|
Conversation conversation = conversations.get(i);
|
||||||
|
if (conversation.getAccount() == account) {
|
||||||
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
|
leaveMuc(conversation);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
conversation.endOtrIfNeeded();
|
||||||
|
} catch (OtrException e) {
|
||||||
|
Log.d(LOGTAG, "error ending otr session for "
|
||||||
|
+ conversation.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
account.getXmppConnection().disconnect();
|
||||||
|
Log.d(LOGTAG, "disconnected account: " + account.getJid());
|
||||||
|
account.setXmppConnection(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return mBinder;
|
return mBinder;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import de.gultsch.chat.R;
|
||||||
import de.gultsch.chat.R.id;
|
import de.gultsch.chat.R.id;
|
||||||
import de.gultsch.chat.entities.Contact;
|
import de.gultsch.chat.entities.Contact;
|
||||||
import de.gultsch.chat.entities.Conversation;
|
import de.gultsch.chat.entities.Conversation;
|
||||||
|
import de.gultsch.chat.entities.Message;
|
||||||
import de.gultsch.chat.utils.UIHelper;
|
import de.gultsch.chat.utils.UIHelper;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -114,7 +115,7 @@ public class ConversationActivity extends XmppActivity {
|
||||||
Collections.sort(this.conversationList, new Comparator<Conversation>() {
|
Collections.sort(this.conversationList, new Comparator<Conversation>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(Conversation lhs, Conversation rhs) {
|
public int compare(Conversation lhs, Conversation rhs) {
|
||||||
return (int) (rhs.getLatestMessageDate() - lhs.getLatestMessageDate());
|
return (int) (rhs.getLatestMessage().getTimeSent() - lhs.getLatestMessage().getTimeSent());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -143,7 +144,7 @@ public class ConversationActivity extends XmppActivity {
|
||||||
TextView convName = (TextView) view.findViewById(R.id.conversation_name);
|
TextView convName = (TextView) view.findViewById(R.id.conversation_name);
|
||||||
convName.setText(conv.getName());
|
convName.setText(conv.getName());
|
||||||
TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg);
|
TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg);
|
||||||
convLastMsg.setText(conv.getLatestMessage());
|
convLastMsg.setText(conv.getLatestMessage().getBody());
|
||||||
|
|
||||||
if(!conv.isRead()) {
|
if(!conv.isRead()) {
|
||||||
convName.setTypeface(null,Typeface.BOLD);
|
convName.setTypeface(null,Typeface.BOLD);
|
||||||
|
@ -154,7 +155,7 @@ public class ConversationActivity extends XmppActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
((TextView) view.findViewById(R.id.conversation_lastupdate))
|
((TextView) view.findViewById(R.id.conversation_lastupdate))
|
||||||
.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessageDate()));
|
.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessage().getTimeSent()));
|
||||||
|
|
||||||
Uri profilePhoto = getItem(position).getProfilePhotoUri();
|
Uri profilePhoto = getItem(position).getProfilePhotoUri();
|
||||||
ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
|
ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
|
||||||
|
@ -238,18 +239,23 @@ public class ConversationActivity extends XmppActivity {
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.conversations, menu);
|
getMenuInflater().inflate(R.menu.conversations, menu);
|
||||||
|
MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
|
||||||
|
|
||||||
if (spl.isOpen()) {
|
if (spl.isOpen()) {
|
||||||
((MenuItem) menu.findItem(R.id.action_archive)).setVisible(false);
|
((MenuItem) menu.findItem(R.id.action_archive)).setVisible(false);
|
||||||
((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
|
((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
|
||||||
((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
|
menuSecure.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
((MenuItem) menu.findItem(R.id.action_add)).setVisible(false);
|
((MenuItem) menu.findItem(R.id.action_add)).setVisible(false);
|
||||||
if (this.getSelectedConversation()!=null) {
|
if (this.getSelectedConversation()!=null) {
|
||||||
if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
|
if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
|
||||||
((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
|
((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
|
||||||
((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
|
menuSecure.setVisible(false);
|
||||||
((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference");
|
((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference");
|
||||||
|
} else {
|
||||||
|
if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) {
|
||||||
|
menuSecure.setIcon(R.drawable.ic_action_secure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import android.graphics.Typeface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
@ -36,38 +35,30 @@ public class ConversationFragment extends Fragment {
|
||||||
protected ArrayAdapter<Message> messageListAdapter;
|
protected ArrayAdapter<Message> messageListAdapter;
|
||||||
protected Contact contact;
|
protected Contact contact;
|
||||||
|
|
||||||
|
private EditText chatMsg;
|
||||||
|
private int nextMessageEncryption = Message.ENCRYPTION_NONE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(final LayoutInflater inflater,
|
public View onCreateView(final LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
|
||||||
this.inflater = inflater;
|
this.inflater = inflater;
|
||||||
|
|
||||||
|
|
||||||
final View view = inflater.inflate(R.layout.fragment_conversation,
|
final View view = inflater.inflate(R.layout.fragment_conversation,
|
||||||
container, false);
|
container, false);
|
||||||
|
chatMsg = (EditText) view.findViewById(R.id.textinput);
|
||||||
((ImageButton) view.findViewById(R.id.textSendButton))
|
((ImageButton) view.findViewById(R.id.textSendButton))
|
||||||
.setOnClickListener(new OnClickListener() {
|
.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
ConversationActivity activity = (ConversationActivity) getActivity();
|
ConversationActivity activity = (ConversationActivity) getActivity();
|
||||||
EditText chatMsg = (EditText) view
|
|
||||||
.findViewById(R.id.textinput);
|
|
||||||
if (chatMsg.getText().length() < 1)
|
if (chatMsg.getText().length() < 1)
|
||||||
return;
|
return;
|
||||||
Message message = new Message(conversation, chatMsg
|
Message message = new Message(conversation, chatMsg
|
||||||
.getText().toString(), Message.ENCRYPTION_NONE);
|
.getText().toString(), nextMessageEncryption);
|
||||||
activity.xmppConnectionService.sendMessage(conversation.getAccount(),message);
|
activity.xmppConnectionService.sendMessage(conversation.getAccount(),message);
|
||||||
chatMsg.setText("");
|
chatMsg.setText("");
|
||||||
|
|
||||||
/*if (conversation.getMode()==Conversation.MODE_SINGLE) {
|
|
||||||
conversation.getMessages().add(message);
|
|
||||||
messageList.add(message);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//activity.updateConversationList();
|
|
||||||
|
|
||||||
//messagesView.setSelection(messageList.size() - 1);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -213,6 +204,22 @@ public class ConversationFragment extends Fragment {
|
||||||
this.messageList.clear();
|
this.messageList.clear();
|
||||||
this.messageList.addAll(this.conversation.getMessages());
|
this.messageList.addAll(this.conversation.getMessages());
|
||||||
this.messageListAdapter.notifyDataSetChanged();
|
this.messageListAdapter.notifyDataSetChanged();
|
||||||
|
if (messageList.size()>=1) {
|
||||||
|
nextMessageEncryption = this.conversation.getLatestMessage().getEncryption();
|
||||||
|
}
|
||||||
|
getActivity().invalidateOptionsMenu();
|
||||||
|
switch (nextMessageEncryption) {
|
||||||
|
case Message.ENCRYPTION_NONE:
|
||||||
|
chatMsg.setHint("Send plain text message");
|
||||||
|
break;
|
||||||
|
case Message.ENCRYPTION_OTR:
|
||||||
|
chatMsg.setHint("Send OTR encrypted message");
|
||||||
|
break;
|
||||||
|
case Message.ENCRYPTION_PGP:
|
||||||
|
chatMsg.setHint("Send openPGP encryted messeage");
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
int size = this.messageList.size();
|
int size = this.messageList.size();
|
||||||
if (size >= 1)
|
if (size >= 1)
|
||||||
messagesView.setSelection(size - 1);
|
messagesView.setSelection(size - 1);
|
||||||
|
|
|
@ -92,7 +92,7 @@ public class UIHelper {
|
||||||
.getName(), (int) res
|
.getName(), (int) res
|
||||||
.getDimension(android.R.dimen.notification_large_icon_width)));
|
.getDimension(android.R.dimen.notification_large_icon_width)));
|
||||||
mBuilder.setContentTitle(conversation.getName());
|
mBuilder.setContentTitle(conversation.getName());
|
||||||
mBuilder.setTicker(conversation.getLatestMessage().trim());
|
mBuilder.setTicker(conversation.getLatestMessage().getBody().trim());
|
||||||
StringBuilder bigText = new StringBuilder();
|
StringBuilder bigText = new StringBuilder();
|
||||||
List<Message> messages = conversation.getMessages();
|
List<Message> messages = conversation.getMessages();
|
||||||
String firstLine = "";
|
String firstLine = "";
|
||||||
|
|
|
@ -3,12 +3,7 @@ package de.gultsch.chat.xml;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class MessagePacket extends Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBody(String text) {
|
public void setBody(String text) {
|
||||||
|
this.children.remove(findChild("body"));
|
||||||
Element body = new Element("body");
|
Element body = new Element("body");
|
||||||
body.setContent(text);
|
body.setContent(text);
|
||||||
this.children.add(body);
|
this.children.add(body);
|
||||||
|
|
|
@ -411,6 +411,7 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessagePacket(MessagePacket packet) {
|
public void sendMessagePacket(MessagePacket packet) {
|
||||||
|
Log.d(LOGTAG,"sending message packet "+packet.toString());
|
||||||
tagWriter.writeElement(packet);
|
tagWriter.writeElement(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,6 +441,6 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
shouldConnect = false;
|
shouldConnect = false;
|
||||||
tagWriter.writeTag(Tag.end("stream"));
|
tagWriter.writeTag(Tag.end("stream:stream"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue