diff options
author | Daniel Gultsch <daniel@gultsch.de> | 2015-11-28 20:11:38 +0100 |
---|---|---|
committer | Daniel Gultsch <daniel@gultsch.de> | 2015-11-28 20:11:38 +0100 |
commit | b7a5254bae90148c4c39c4e2102d4b2ff07600c0 (patch) | |
tree | c9f671128421b55f0549c8dd6a52ba58831075c9 | |
parent | aeba964a65fce4ac77c908533d25295d44e9d2b5 (diff) |
initial tor support
15 files changed, 346 insertions, 130 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 046dd0d5..ddeba611 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -2,6 +2,10 @@ package eu.siacs.conversations; import android.graphics.Bitmap; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; + import eu.siacs.conversations.xmpp.chatstate.ChatState; public final class Config { diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index b02db812..3aa1675f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -2,6 +2,8 @@ package eu.siacs.conversations.entities; import android.content.ContentValues; import android.database.Cursor; +import android.os.Bundle; +import android.os.Parcelable; import android.os.SystemClock; import eu.siacs.conversations.crypto.PgpDecryptionService; @@ -13,6 +15,7 @@ import org.json.JSONObject; import java.security.PublicKey; import java.security.interfaces.DSAPublicKey; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -39,6 +42,8 @@ public class Account extends AbstractEntity { public static final String KEYS = "keys"; public static final String AVATAR = "avatar"; public static final String DISPLAY_NAME = "display_name"; + public static final String HOSTNAME = "hostname"; + public static final String PORT = "port"; public static final String PINNED_MECHANISM_KEY = "pinned_mechanism"; @@ -67,7 +72,20 @@ public class Account extends AbstractEntity { } } - public static enum State { + public ArrayList<Parcelable> getHostnamePortBundles() { + ArrayList<Parcelable> values = new ArrayList<>(); + Bundle hostPort = new Bundle(); + if (hostname != null && !hostname.isEmpty()) { + hostPort.putString("name", hostname); + } else { + hostPort.putString("name", getServer().toString()); + } + hostPort.putInt("port", port); + values.add(hostPort); + return values; + } + + public enum State { DISABLED, OFFLINE, CONNECTING, @@ -147,6 +165,8 @@ public class Account extends AbstractEntity { protected JSONObject keys = new JSONObject(); protected String avatar; protected String displayName = null; + protected String hostname = null; + protected int port = 5222; protected boolean online = false; private OtrService mOtrService = null; private AxolotlService axolotlService = null; @@ -164,12 +184,12 @@ public class Account extends AbstractEntity { public Account(final Jid jid, final String password) { this(java.util.UUID.randomUUID().toString(), jid, - password, 0, null, "", null, null); + password, 0, null, "", null, null, null, 5222); } public Account(final String uuid, final Jid jid, final String password, final int options, final String rosterVersion, final String keys, - final String avatar, String displayName) { + final String avatar, String displayName, String hostname, int port) { this.uuid = uuid; this.jid = jid; if (jid.isBareJid()) { @@ -185,6 +205,8 @@ public class Account extends AbstractEntity { } this.avatar = avatar; this.displayName = displayName; + this.hostname = hostname; + this.port = port; } public static Account fromCursor(final Cursor cursor) { @@ -201,7 +223,9 @@ public class Account extends AbstractEntity { cursor.getString(cursor.getColumnIndex(ROSTERVERSION)), cursor.getString(cursor.getColumnIndex(KEYS)), cursor.getString(cursor.getColumnIndex(AVATAR)), - cursor.getString(cursor.getColumnIndex(DISPLAY_NAME))); + cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)), + cursor.getString(cursor.getColumnIndex(HOSTNAME)), + cursor.getInt(cursor.getColumnIndex(PORT))); } public boolean isOptionSet(final int option) { @@ -236,6 +260,22 @@ public class Account extends AbstractEntity { this.password = password; } + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public String getHostname() { + return this.hostname == null ? "" : this.hostname; + } + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return this.port; + } + public State getStatus() { if (isOptionSet(OPTION_DISABLED)) { return State.DISABLED; @@ -314,6 +354,8 @@ public class Account extends AbstractEntity { values.put(ROSTERVERSION, rosterVersion); values.put(AVATAR, avatar); values.put(DISPLAY_NAME, displayName); + values.put(HOSTNAME, hostname); + values.put(PORT, port); return values; } diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java index 90fbadfe..6435fd47 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java @@ -1,7 +1,13 @@ package eu.siacs.conversations.http; +import android.os.Build; + import org.apache.http.conn.ssl.StrictHostnameVerifier; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.List; @@ -87,4 +93,12 @@ public class HttpConnectionManager extends AbstractConnectionManager { } catch (final KeyManagementException | NoSuchAlgorithmException ignored) { } } + + public Proxy getProxy() throws IOException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(InetAddress.getLocalHost(), 9050)); + } else { + return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(InetAddress.getLocalHost(), 8118)); + } + } } diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index 96dee62c..f371a255 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -10,7 +10,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URL; import java.util.Arrays; @@ -39,10 +42,12 @@ public class HttpDownloadConnection implements Transferable { private int mStatus = Transferable.STATUS_UNKNOWN; private boolean acceptedAutomatically = false; private int mProgress = 0; + private boolean mUseTor = false; public HttpDownloadConnection(HttpConnectionManager manager) { this.mHttpConnectionManager = manager; this.mXmppConnectionService = manager.getXmppConnectionService(); + this.mUseTor = mXmppConnectionService.useTorToConnect(); } @Override @@ -191,8 +196,15 @@ public class HttpDownloadConnection implements Transferable { try { Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive)); changeStatus(STATUS_CHECKING); - HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); + HttpURLConnection connection; + if (mUseTor) { + connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy()); + } else { + connection = (HttpURLConnection) mUrl.openConnection(); + } connection.setRequestMethod("HEAD"); + Log.d(Config.LOGTAG,"url: "+connection.getURL().toString()); + Log.d(Config.LOGTAG,"connection: "+connection.toString()); connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName()); if (connection instanceof HttpsURLConnection) { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); @@ -245,7 +257,12 @@ public class HttpDownloadConnection implements Transferable { PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid()); try { wakeLock.acquire(); - HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); + HttpURLConnection connection; + if (mUseTor) { + connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy()); + } else { + connection = (HttpURLConnection) mUrl.openConnection(); + } if (connection instanceof HttpsURLConnection) { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); } diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index 3aa28881..1e4b9102 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -12,7 +12,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URL; import javax.net.ssl.HttpsURLConnection; @@ -46,6 +49,7 @@ public class HttpUploadConnection implements Transferable { private String mime; private URL mGetUrl; private URL mPutUrl; + private boolean mUseTor = false; private byte[] key = null; @@ -56,6 +60,7 @@ public class HttpUploadConnection implements Transferable { public HttpUploadConnection(HttpConnectionManager httpConnectionManager) { this.mHttpConnectionManager = httpConnectionManager; this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); + this.mUseTor = mXmppConnectionService.useTorToConnect(); } @Override @@ -158,7 +163,11 @@ public class HttpUploadConnection implements Transferable { try { wakeLock.acquire(); Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()); - connection = (HttpURLConnection) mPutUrl.openConnection(); + if (mUseTor) { + connection = (HttpURLConnection) mPutUrl.openConnection(mHttpConnectionManager.getProxy()); + } else { + connection = (HttpURLConnection) mPutUrl.openConnection(); + } if (connection instanceof HttpsURLConnection) { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 0e133d19..8b74581c 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -43,7 +43,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 19; + private static final int DATABASE_VERSION = 20; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -59,41 +59,41 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE " + SQLiteAxolotlStore.PREKEY_TABLENAME + "(" - + SQLiteAxolotlStore.ACCOUNT + " TEXT, " - + SQLiteAxolotlStore.ID + " INTEGER, " - + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" - + SQLiteAxolotlStore.ACCOUNT - + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " - + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " - + SQLiteAxolotlStore.ID - + ") ON CONFLICT REPLACE" - +");"; + + SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + SQLiteAxolotlStore.ID + " INTEGER, " + + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " + + SQLiteAxolotlStore.ID + + ") ON CONFLICT REPLACE" + + ");"; private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE " + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "(" - + SQLiteAxolotlStore.ACCOUNT + " TEXT, " - + SQLiteAxolotlStore.ID + " INTEGER, " - + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" - + SQLiteAxolotlStore.ACCOUNT - + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " - + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " - + SQLiteAxolotlStore.ID - + ") ON CONFLICT REPLACE"+ + + SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + SQLiteAxolotlStore.ID + " INTEGER, " + + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " + + SQLiteAxolotlStore.ID + + ") ON CONFLICT REPLACE" + ");"; private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE " + SQLiteAxolotlStore.SESSION_TABLENAME + "(" - + SQLiteAxolotlStore.ACCOUNT + " TEXT, " - + SQLiteAxolotlStore.NAME + " TEXT, " - + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, " - + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" - + SQLiteAxolotlStore.ACCOUNT - + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " - + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " - + SQLiteAxolotlStore.NAME + ", " - + SQLiteAxolotlStore.DEVICE_ID - + ") ON CONFLICT REPLACE" - +");"; + + SQLiteAxolotlStore.ACCOUNT + " TEXT, " + + SQLiteAxolotlStore.NAME + " TEXT, " + + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, " + + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY(" + + SQLiteAxolotlStore.ACCOUNT + + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " + + SQLiteAxolotlStore.NAME + ", " + + SQLiteAxolotlStore.DEVICE_ID + + ") ON CONFLICT REPLACE" + + ");"; private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE " + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "(" @@ -106,10 +106,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { + SQLiteAxolotlStore.ACCOUNT + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, " + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", " - + SQLiteAxolotlStore.NAME + ", " - + SQLiteAxolotlStore.FINGERPRINT + + SQLiteAxolotlStore.NAME + ", " + + SQLiteAxolotlStore.FINGERPRINT + ") ON CONFLICT IGNORE" - +");"; + + ");"; private DatabaseBackend(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -124,7 +124,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Account.DISPLAY_NAME + " TEXT, " + Account.ROSTERVERSION + " TEXT," + Account.OPTIONS + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS - + " TEXT)"); + + " TEXT, " + Account.HOSTNAME + " TEXT, " + Account.PORT + " NUMBER DEFAULT 5222)"); db.execSQL("create table " + Conversation.TABLENAME + " (" + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME + " TEXT, " + Conversation.CONTACT + " TEXT, " @@ -202,23 +202,23 @@ public class DatabaseBackend extends SQLiteOpenHelper { if (oldVersion < 11 && newVersion >= 11) { db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.GROUPS + " TEXT"); - db.execSQL("delete from "+Contact.TABLENAME); - db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL"); + db.execSQL("delete from " + Contact.TABLENAME); + db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL"); } if (oldVersion < 12 && newVersion >= 12) { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.SERVER_MSG_ID + " TEXT"); } if (oldVersion < 13 && newVersion >= 13) { - db.execSQL("delete from "+Contact.TABLENAME); - db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL"); + db.execSQL("delete from " + Contact.TABLENAME); + db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL"); } if (oldVersion < 14 && newVersion >= 14) { // migrate db to new, canonicalized JID domainpart representation // Conversation table Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]); - while(cursor.moveToNext()) { + while (cursor.moveToNext()) { String newJid; try { newJid = Jid.fromString( @@ -226,8 +226,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { ).toString(); } catch (InvalidJidException ignored) { Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID " - +cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)) - +": " + ignored +". Skipping..."); + + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)) + + ": " + ignored + ". Skipping..."); continue; } @@ -236,14 +236,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.getString(cursor.getColumnIndex(Conversation.UUID)), }; db.execSQL("update " + Conversation.TABLENAME - + " set " + Conversation.CONTACTJID + " = ? " + + " set " + Conversation.CONTACTJID + " = ? " + " where " + Conversation.UUID + " = ?", updateArgs); } cursor.close(); // Contact table cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]); - while(cursor.moveToNext()) { + while (cursor.moveToNext()) { String newJid; try { newJid = Jid.fromString( @@ -251,8 +251,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { ).toString(); } catch (InvalidJidException ignored) { Log.e(Config.LOGTAG, "Failed to migrate Contact JID " - +cursor.getString(cursor.getColumnIndex(Contact.JID)) - +": " + ignored +". Skipping..."); + + cursor.getString(cursor.getColumnIndex(Contact.JID)) + + ": " + ignored + ". Skipping..."); continue; } @@ -270,7 +270,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { // Account table cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]); - while(cursor.moveToNext()) { + while (cursor.moveToNext()) { String newServer; try { newServer = Jid.fromParts( @@ -280,8 +280,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { ).getDomainpart(); } catch (InvalidJidException ignored) { Log.e(Config.LOGTAG, "Failed to migrate Account SERVER " - +cursor.getString(cursor.getColumnIndex(Account.SERVER)) - +": " + ignored +". Skipping..."); + + cursor.getString(cursor.getColumnIndex(Account.SERVER)) + + ": " + ignored + ". Skipping..."); continue; } @@ -295,7 +295,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } cursor.close(); } - if (oldVersion < 15 && newVersion >= 15) { + if (oldVersion < 15 && newVersion >= 15) { recreateAxolotlDb(db); db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.FINGERPRINT + " TEXT"); @@ -305,7 +305,11 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Message.CARBON + " INTEGER"); } if (oldVersion < 19 && newVersion >= 19) { - db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "+ Account.DISPLAY_NAME+ " TEXT"); + db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT"); + } + if (oldVersion < 20 && newVersion >= 20) { + db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT"); + db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222"); } /* Any migrations that alter the Account table need to happen BEFORE this migration, as it * depends on account de-serialization. @@ -314,7 +318,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { List<Account> accounts = getAccounts(db); for (Account account : accounts) { String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID); - if ( ownDeviceIdString == null ) { + if (ownDeviceIdString == null) { continue; } int ownDeviceId = Integer.valueOf(ownDeviceIdString); @@ -324,12 +328,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { if (identityKeyPair != null) { setIdentityKeyTrust(db, account, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), XmppAxolotlSession.Trust.TRUSTED); } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not load own identity key pair"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair"); } } } if (oldVersion < 18 && newVersion >= 18) { - db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "+ Message.READ+ " NUMBER DEFAULT 1"); + db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1"); } } @@ -374,7 +378,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public CopyOnWriteArrayList<Conversation> getConversations(int status) { CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { Integer.toString(status) }; + String[] selectionArgs = {Integer.toString(status)}; Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME + " where " + Conversation.STATUS + " = ? order by " + Conversation.CREATED + " desc", selectionArgs); @@ -390,20 +394,20 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public ArrayList<Message> getMessages(Conversation conversation, int limit, - long timestamp) { + long timestamp) { ArrayList<Message> list = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; if (timestamp == -1) { - String[] selectionArgs = { conversation.getUuid() }; + String[] selectionArgs = {conversation.getUuid()}; cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION + "=?", selectionArgs, null, null, Message.TIME_SENT + " DESC", String.valueOf(limit)); } else { - String[] selectionArgs = { conversation.getUuid(), - Long.toString(timestamp) }; + String[] selectionArgs = {conversation.getUuid(), + Long.toString(timestamp)}; cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION - + "=? and " + Message.TIME_SENT + "<?", selectionArgs, + + "=? and " + Message.TIME_SENT + "<?", selectionArgs, null, null, Message.TIME_SENT + " DESC", String.valueOf(limit)); } @@ -419,13 +423,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { return list; } - public Iterable<Message> getMessagesIterable(final Conversation conversation){ + public Iterable<Message> getMessagesIterable(final Conversation conversation) { return new Iterable<Message>() { @Override public Iterator<Message> iterator() { - class MessageIterator implements Iterator<Message>{ + class MessageIterator implements Iterator<Message> { SQLiteDatabase db = getReadableDatabase(); - String[] selectionArgs = { conversation.getUuid() }; + String[] selectionArgs = {conversation.getUuid()}; Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION + "=?", selectionArgs, null, null, Message.TIME_SENT + " ASC", null); @@ -458,10 +462,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { public Conversation findConversation(final Account account, final Jid contactJid) { SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { account.getUuid(), + String[] selectionArgs = {account.getUuid(), contactJid.toBareJid().toString() + "/%", contactJid.toBareJid().toString() - }; + }; Cursor cursor = db.query(Conversation.TABLENAME, null, Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null); @@ -475,7 +479,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void updateConversation(final Conversation conversation) { final SQLiteDatabase db = this.getWritableDatabase(); - final String[] args = { conversation.getUuid() }; + final String[] args = {conversation.getUuid()}; db.update(Conversation.TABLENAME, conversation.getContentValues(), Conversation.UUID + "=?", args); } @@ -498,14 +502,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void updateAccount(Account account) { SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { account.getUuid() }; + String[] args = {account.getUuid()}; db.update(Account.TABLENAME, account.getContentValues(), Account.UUID + "=?", args); } public void deleteAccount(Account account) { SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { account.getUuid() }; + String[] args = {account.getUuid()}; db.delete(Account.TABLENAME, Account.UUID + "=?", args); } @@ -534,7 +538,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void updateMessage(Message message) { SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { message.getUuid() }; + String[] args = {message.getUuid()}; db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); } @@ -542,7 +546,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void readRoster(Roster roster) { SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; - String args[] = { roster.getAccount().getUuid() }; + String args[] = {roster.getAccount().getUuid()}; cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null); while (cursor.moveToNext()) { roster.initContact(Contact.fromCursor(cursor)); @@ -558,7 +562,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.insert(Contact.TABLENAME, null, contact.getContentValues()); } else { String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?"; - String[] whereArgs = { account.getUuid(), contact.getJid().toString() }; + String[] whereArgs = {account.getUuid(), contact.getJid().toString()}; db.delete(Contact.TABLENAME, where, whereArgs); } } @@ -568,19 +572,19 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void deleteMessage(Message message) { SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { message.getUuid() }; + String[] args = {message.getUuid()}; db.delete(Message.TABLENAME, Message.UUID + "=?", args); } public void deleteMessagesInConversation(Conversation conversation) { SQLiteDatabase db = this.getWritableDatabase(); - String[] args = { conversation.getUuid() }; + String[] args = {conversation.getUuid()}; db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); } public Conversation findConversationByUuid(String conversationUuid) { SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { conversationUuid }; + String[] selectionArgs = {conversationUuid}; Cursor cursor = db.query(Conversation.TABLENAME, null, Conversation.UUID + "=?", selectionArgs, null, null, null); if (cursor.getCount() == 0) { @@ -594,7 +598,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public Message findMessageByUuid(String messageUuid) { SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { messageUuid }; + String[] selectionArgs = {messageUuid}; Cursor cursor = db.query(Message.TABLENAME, null, Message.UUID + "=?", selectionArgs, null, null, null); if (cursor.getCount() == 0) { @@ -608,7 +612,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public Account findAccountByUuid(String accountUuid) { SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { accountUuid }; + String[] selectionArgs = {accountUuid}; Cursor cursor = db.query(Account.TABLENAME, null, Account.UUID + "=?", selectionArgs, null, null, null); if (cursor.getCount() == 0) { @@ -624,9 +628,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { ArrayList<Message> list = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; - String[] selectionArgs = { conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE) }; - cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION - + "=? AND "+Message.TYPE+"=?", selectionArgs, null, null,null); + String[] selectionArgs = {conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE)}; + cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION + + "=? AND " + Message.TYPE + "=?", selectionArgs, null, null, null); if (cursor.getCount() > 0) { cursor.moveToLast(); do { @@ -659,10 +663,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { public SessionRecord loadSession(Account account, AxolotlAddress contact) { SessionRecord session = null; Cursor cursor = getCursorForSession(account, contact); - if(cursor.getCount() != 0) { + if (cursor.getCount() != 0) { cursor.moveToFirst(); try { - session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); + session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)); } catch (IOException e) { cursor.close(); throw new AssertionError(e); @@ -689,7 +693,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { selectionArgs, null, null, null); - while(cursor.moveToNext()) { + while (cursor.moveToNext()) { devices.add(cursor.getInt( cursor.getColumnIndex(SQLiteAxolotlStore.DEVICE_ID))); } @@ -710,7 +714,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { ContentValues values = new ContentValues(); values.put(SQLiteAxolotlStore.NAME, contact.getName()); values.put(SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId()); - values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(),Base64.DEFAULT)); + values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(), Base64.DEFAULT)); values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid()); db.insert(SQLiteAxolotlStore.SESSION_TABLENAME, null, values); } @@ -757,11 +761,11 @@ public class DatabaseBackend extends SQLiteOpenHelper { public PreKeyRecord loadPreKey(Account account, int preKeyId) { PreKeyRecord record = null; Cursor cursor = getCursorForPreKey(account, preKeyId); - if(cursor.getCount() != 0) { + if (cursor.getCount() != 0) { cursor.moveToFirst(); try { - record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); - } catch (IOException e ) { + record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)); + } catch (IOException e) { throw new AssertionError(e); } } @@ -780,7 +784,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(SQLiteAxolotlStore.ID, record.getId()); - values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(),Base64.DEFAULT)); + values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT)); values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid()); db.insert(SQLiteAxolotlStore.PREKEY_TABLENAME, null, values); } @@ -810,11 +814,11 @@ public class DatabaseBackend extends SQLiteOpenHelper { public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) { SignedPreKeyRecord record = null; Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId); - if(cursor.getCount() != 0) { + if (cursor.getCount() != 0) { cursor.moveToFirst(); try { - record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); - } catch (IOException e ) { + record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)); + } catch (IOException e) { throw new AssertionError(e); } } @@ -833,7 +837,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { selectionArgs, null, null, null); - while(cursor.moveToNext()) { + while (cursor.moveToNext()) { try { prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT))); } catch (IOException ignored) { @@ -854,7 +858,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(SQLiteAxolotlStore.ID, record.getId()); - values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(),Base64.DEFAULT)); + values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT)); values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid()); db.insert(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values); } @@ -874,7 +878,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, boolean own) { - return getIdentityKeyCursor(db, account, name, own, null); + return getIdentityKeyCursor(db, account, name, own, null); } private Cursor getIdentityKeyCursor(Account account, String fingerprint) { @@ -892,16 +896,16 @@ public class DatabaseBackend extends SQLiteOpenHelper { ArrayList<String> selectionArgs = new ArrayList<>(4); selectionArgs.add(account.getUuid()); String selectionString = SQLiteAxolotlStore.ACCOUNT + " = ?"; - if (name != null){ + if (name != null) { selectionArgs.add(name); selectionString += " AND " + SQLiteAxolotlStore.NAME + " = ?"; } - if (fingerprint != null){ + if (fingerprint != null) { selectionArgs.add(fingerprint); selectionString += " AND " + SQLiteAxolotlStore.FINGERPRINT + " = ?"; } - if (own != null){ - selectionArgs.add(own?"1":"0"); + if (own != null) { + selectionArgs.add(own ? "1" : "0"); selectionString += " AND " + SQLiteAxolotlStore.OWN + " = ?"; } Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, @@ -922,12 +926,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { String name = account.getJid().toBareJid().toString(); IdentityKeyPair identityKeyPair = null; Cursor cursor = getIdentityKeyCursor(db, account, name, true); - if(cursor.getCount() != 0) { + if (cursor.getCount() != 0) { cursor.moveToFirst(); try { - identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)),Base64.DEFAULT)); + identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)); } catch (InvalidKeyException e) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); } } cursor.close(); @@ -943,16 +947,16 @@ public class DatabaseBackend extends SQLiteOpenHelper { Set<IdentityKey> identityKeys = new HashSet<>(); Cursor cursor = getIdentityKeyCursor(account, name, false); - while(cursor.moveToNext()) { - if ( trust != null && + while (cursor.moveToNext()) { + if (trust != null && cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.TRUSTED)) != trust.getCode()) { continue; } try { - identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)),Base64.DEFAULT),0)); + identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT), 0)); } catch (InvalidKeyException e) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Encountered invalid IdentityKey in database for account"+account.getJid().toBareJid()+", address: "+name); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); } } cursor.close(); @@ -970,8 +974,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { }; return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME, SQLiteAxolotlStore.ACCOUNT + " = ?" - + " AND " + SQLiteAxolotlStore.NAME + " = ?" - + " AND (" + SQLiteAxolotlStore.TRUSTED + " = ? OR "+SQLiteAxolotlStore.TRUSTED+ " = ?)", + + " AND " + SQLiteAxolotlStore.NAME + " = ?" + + " AND (" + SQLiteAxolotlStore.TRUSTED + " = ? OR " + SQLiteAxolotlStore.TRUSTED + " = ?)", args ); } @@ -1018,7 +1022,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { values.put(SQLiteAxolotlStore.TRUSTED, trust.getCode()); int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values, SQLiteAxolotlStore.ACCOUNT + " = ? AND " - + SQLiteAxolotlStore.FINGERPRINT + " = ? ", + + SQLiteAxolotlStore.FINGERPRINT + " = ? ", selectionArgs); return rows == 1; } @@ -1036,7 +1040,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void recreateAxolotlDb(SQLiteDatabase db) { - Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+">>> (RE)CREATING AXOLOTL DATABASE <<<"); + Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<"); db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME); db.execSQL(CREATE_SESSIONS_STATEMENT); db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.PREKEY_TABLENAME); @@ -1046,12 +1050,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.IDENTITIES_TABLENAME); db.execSQL(CREATE_IDENTITIES_STATEMENT); } - + public void wipeAxolotlDb(Account account) { String accountName = account.getUuid(); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<"); + Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<"); SQLiteDatabase db = this.getWritableDatabase(); - String[] deleteArgs= { + String[] deleteArgs = { accountName }; db.delete(SQLiteAxolotlStore.SESSION_TABLENAME, diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 4bde7fb6..30f8c687 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -2106,8 +2106,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void createContact(Contact contact) { - SharedPreferences sharedPref = getPreferences(); - boolean autoGrant = sharedPref.getBoolean("grant_new_contacts", true); + boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true); if (autoGrant) { contact.setOption(Contact.Options.PREEMPTIVE_GRANT); contact.setOption(Contact.Options.ASKING); @@ -2534,10 +2533,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa .getDefaultSharedPreferences(getApplicationContext()); } - public boolean forceEncryption() { - return getPreferences().getBoolean("force_encryption", false); - } - public boolean confirmMessages() { return getPreferences().getBoolean("confirm_messages", true); } @@ -2554,6 +2549,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return getPreferences().getBoolean("indicate_received", false); } + public boolean useTorToConnect() { + return getPreferences().getBoolean("use_tor", false); + } + public int unreadCount() { int count = 0; for (Conversation conversation : getConversations()) { diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 5fe5848c..bbdc7524 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -82,10 +82,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate private ImageButton mRegenerateAxolotlKeyButton; private LinearLayout keys; private LinearLayout keysCard; + private LinearLayout mNamePort; + private EditText mHostname; + private EditText mPort; private AlertDialog mCaptchaDialog = null; private Jid jidToEdit; private boolean mInitMode = false; + private boolean mUseTor = false; private Account mAccount; private String messageFingerprint; @@ -136,6 +140,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } final String password = mPassword.getText().toString(); final String passwordConfirm = mPasswordConfirm.getText().toString(); + final String hostname = mHostname.getText().toString(); + final String port = mPort.getText().toString(); if (registerNewAccount) { if (!password.equals(passwordConfirm)) { mPasswordConfirm.setError(getString(R.string.passwords_do_not_match)); @@ -149,6 +155,25 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate mPasswordConfirm.setError(null); mAccount.setPassword(password); mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); + if (hostname.contains(" ")) { + mHostname.setError(getString(R.string.not_valid_hostname)); + mHostname.requestFocus(); + return; + } + mAccount.setHostname(hostname); + try { + int numericPort = Integer.parseInt(port); + if (numericPort < 0 || numericPort > 65535) { + mPort.setError(getString(R.string.not_a_valid_port)); + mPort.requestFocus(); + return; + } + mAccount.setPort(numericPort); + } catch (NumberFormatException e) { + mPort.setError(getString(R.string.not_a_valid_port)); + mPort.requestFocus(); + return; + } xmppConnectionService.updateAccount(mAccount); } else { if (xmppConnectionService.findAccountByJid(jid) != null) { @@ -319,7 +344,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate unmodified = this.mAccount.getJid().toBareJid().toString(); } return !unmodified.equals(this.mAccountJid.getText().toString()) || - !this.mAccount.getPassword().equals(this.mPassword.getText().toString()); + !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) || + !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) || + !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString()); } @Override @@ -368,6 +395,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_axolotl_key); this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card); this.keys = (LinearLayout) findViewById(R.id.other_device_keys); + this.mNamePort = (LinearLayout) findViewById(R.id.name_port); + this.mHostname = (EditText) findViewById(R.id.hostname); + this.mHostname.addTextChangedListener(mTextWatcher); + this.mPort = (EditText) findViewById(R.id.port); + this.mPort.setText("5222"); + this.mPort.addTextChangedListener(mTextWatcher); this.mSaveButton = (Button) findViewById(R.id.save_button); this.mCancelButton = (Button) findViewById(R.id.cancel_button); this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener); @@ -448,6 +481,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } } } + this.mUseTor = getPreferences().getBoolean("use_tor", false); + this.mNamePort.setVisibility(mUseTor ? View.VISIBLE : View.GONE); } @Override @@ -529,6 +564,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString()); } this.mPassword.setText(this.mAccount.getPassword()); + this.mHostname.setText(""); + this.mHostname.getEditableText().append(this.mAccount.getHostname()); + this.mPort.setText(""); + this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort())); + this.mNamePort.setVisibility(mUseTor ? View.VISIBLE : View.GONE); + } if (!mInitMode) { this.mAvatar.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index eca7c0c1..7118eb5a 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -160,6 +160,8 @@ public class SettingsActivity extends XmppActivity implements } else if (name.equals("dont_trust_system_cas")) { xmppConnectionService.updateMemorizingTrustmanager(); reconnectAccounts(); + } else if (name.equals("use_tor")) { + reconnectAccounts(); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 16b3f78b..dec3cad6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -27,6 +27,7 @@ import java.net.ConnectException; import java.net.IDN; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.Socket; import java.net.UnknownHostException; import java.net.URL; @@ -233,16 +234,23 @@ public class XmppConnection implements Runnable { tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); this.changeStatus(Account.State.CONNECTING); + final boolean useTor = mXmppConnectionService.useTorToConnect(); + final Proxy TOR_PROXY = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(InetAddress.getLocalHost(), 9050)); if (DNSHelper.isIp(account.getServer().toString())) { - socket = new Socket(); + socket = useTor ? new Socket(TOR_PROXY) : new Socket(); try { socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); } catch (IOException e) { throw new UnknownHostException(); } } else { - final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); - final ArrayList<Parcelable> values = result.getParcelableArrayList("values"); + final ArrayList<Parcelable> values; + if (useTor) { + values = account.getHostnamePortBundles(); + } else { + final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); + values = result.getParcelableArrayList("values"); + } int i = 0; boolean socketError = true; while (socketError && values.size() > i) { @@ -269,11 +277,11 @@ public class XmppConnection implements Runnable { + ": using values from dns " + srvRecordServer + ":" + srvRecordPort); } - socket = new Socket(); + socket = useTor ? new Socket(TOR_PROXY) : new Socket(); socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socketError = false; } catch (final Throwable e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")"); i++; } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 556395ae..74306ce3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -7,7 +7,9 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; @@ -59,7 +61,9 @@ public class JingleSocks5Transport extends JingleTransport { @Override public void run() { try { - socket = new Socket(); + final boolean useTor = connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); + final Proxy TOR_PROXY = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(InetAddress.getLocalHost(), 9050)); + socket = useTor ? new Socket(TOR_PROXY) : new Socket(); SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort()); socket.connect(address,Config.SOCKET_TIMEOUT * 1000); inputStream = socket.getInputStream(); diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml index 8d60f5f1..73395bba 100644 --- a/src/main/res/layout/activity_edit_account.xml +++ b/src/main/res/layout/activity_edit_account.xml @@ -78,7 +78,59 @@ android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody" /> - <CheckBox + <LinearLayout + android:id="@+id/name_port" + android:layout_marginTop="8dp" + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:weightSum="1"> + <LinearLayout + android:orientation="vertical" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="0.8"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/account_settings_hostname" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" + android:id="@+id/textView"/> + <EditText + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textColor="@color/black87" + android:textColorHint="@color/black54" + android:textSize="?attr/TextSizeBody" + android:id="@+id/hostname" + android:inputType="textNoSuggestions" + android:hint="@string/hostname_or_onion"/> + </LinearLayout> + <LinearLayout + android:orientation="vertical" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="0.2" + > + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/account_settings_port" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody"/> + <EditText + android:layout_width="match_parent" + android:layout_height="match_parent" + android:inputType="number" + android:maxLength="5" + android:textColor="@color/black87" + android:textColorHint="@color/black54" + android:textSize="?attr/TextSizeBody" + android:id="@+id/port"/> + </LinearLayout> + </LinearLayout> + <CheckBox android:id="@+id/account_register_new" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml index 62981a45..b7a40418 100644 --- a/src/main/res/menu/editaccount.xml +++ b/src/main/res/menu/editaccount.xml @@ -31,4 +31,9 @@ <item android:id="@+id/action_clear_devices" android:title="@string/clear_other_devices" android:showAsAction="never"/> + <item + android:id="@+id/action_settings" + android:orderInCategory="100" + android:showAsAction="never" + android:title="@string/action_settings"/> </menu>
\ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index fa484a82..eaff3534 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -540,4 +540,12 @@ <string name="error_fetching_omemo_key">Error fetching OMEMO key!</string> <string name="verified_omemo_key_with_certificate">Verified OMEMO key with certificate!</string> <string name="device_does_not_support_certificates">Your device does not support the selection of client certificates!</string> + <string name="pref_connection_options">Connection options</string> + <string name="pref_use_tor">Connect via Tor</string> + <string name="pref_use_tor_summary">Tunnel all connections through the TOR network. Requires Orbot</string> + <string name="account_settings_hostname">Hostname</string> + <string name="account_settings_port">Port</string> + <string name="hostname_or_onion">Server- or .onion-Address</string> + <string name="not_a_valid_port">This is not a valid port number</string> + <string name="not_valid_hostname">This is not a valid hostname</string> </resources> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index f05738ac..6ef3c029 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -147,6 +147,13 @@ android:summary="@string/pref_remove_trusted_certificates_summary" android:title="@string/pref_remove_trusted_certificates_title"/> </PreferenceCategory> + <PreferenceCategory android:title="@string/pref_connection_options"> + <CheckBoxPreference + android:defaultValue="false" + android:key="use_tor" + android:title="@string/pref_use_tor" + android:summary="@string/pref_use_tor_summary"/> + </PreferenceCategory> <PreferenceCategory android:title="@string/pref_input_options"> <CheckBoxPreference android:defaultValue="false" |