initial tor support

This commit is contained in:
Daniel Gultsch 2015-11-28 20:11:38 +01:00
parent 06cadab7cc
commit f0b1761ec3
15 changed files with 346 additions and 130 deletions

View file

@ -2,6 +2,10 @@ package eu.siacs.conversations;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.chatstate.ChatState;
public final class Config { public final class Config {

View file

@ -2,6 +2,8 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock; import android.os.SystemClock;
import eu.siacs.conversations.crypto.PgpDecryptionService; import eu.siacs.conversations.crypto.PgpDecryptionService;
@ -13,6 +15,7 @@ import org.json.JSONObject;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.interfaces.DSAPublicKey; import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@ -39,6 +42,8 @@ public class Account extends AbstractEntity {
public static final String KEYS = "keys"; public static final String KEYS = "keys";
public static final String AVATAR = "avatar"; public static final String AVATAR = "avatar";
public static final String DISPLAY_NAME = "display_name"; 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"; 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, DISABLED,
OFFLINE, OFFLINE,
CONNECTING, CONNECTING,
@ -147,6 +165,8 @@ public class Account extends AbstractEntity {
protected JSONObject keys = new JSONObject(); protected JSONObject keys = new JSONObject();
protected String avatar; protected String avatar;
protected String displayName = null; protected String displayName = null;
protected String hostname = null;
protected int port = 5222;
protected boolean online = false; protected boolean online = false;
private OtrService mOtrService = null; private OtrService mOtrService = null;
private AxolotlService axolotlService = null; private AxolotlService axolotlService = null;
@ -164,12 +184,12 @@ public class Account extends AbstractEntity {
public Account(final Jid jid, final String password) { public Account(final Jid jid, final String password) {
this(java.util.UUID.randomUUID().toString(), jid, 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, public Account(final String uuid, final Jid jid,
final String password, final int options, final String rosterVersion, final String keys, 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.uuid = uuid;
this.jid = jid; this.jid = jid;
if (jid.isBareJid()) { if (jid.isBareJid()) {
@ -185,6 +205,8 @@ public class Account extends AbstractEntity {
} }
this.avatar = avatar; this.avatar = avatar;
this.displayName = displayName; this.displayName = displayName;
this.hostname = hostname;
this.port = port;
} }
public static Account fromCursor(final Cursor cursor) { 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(ROSTERVERSION)),
cursor.getString(cursor.getColumnIndex(KEYS)), cursor.getString(cursor.getColumnIndex(KEYS)),
cursor.getString(cursor.getColumnIndex(AVATAR)), 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) { public boolean isOptionSet(final int option) {
@ -236,6 +260,22 @@ public class Account extends AbstractEntity {
this.password = password; 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() { public State getStatus() {
if (isOptionSet(OPTION_DISABLED)) { if (isOptionSet(OPTION_DISABLED)) {
return State.DISABLED; return State.DISABLED;
@ -314,6 +354,8 @@ public class Account extends AbstractEntity {
values.put(ROSTERVERSION, rosterVersion); values.put(ROSTERVERSION, rosterVersion);
values.put(AVATAR, avatar); values.put(AVATAR, avatar);
values.put(DISPLAY_NAME, displayName); values.put(DISPLAY_NAME, displayName);
values.put(HOSTNAME, hostname);
values.put(PORT, port);
return values; return values;
} }

View file

@ -1,7 +1,13 @@
package eu.siacs.conversations.http; package eu.siacs.conversations.http;
import android.os.Build;
import org.apache.http.conn.ssl.StrictHostnameVerifier; 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.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.List; import java.util.List;
@ -87,4 +93,12 @@ public class HttpConnectionManager extends AbstractConnectionManager {
} catch (final KeyManagementException | NoSuchAlgorithmException ignored) { } 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));
}
}
} }

View file

@ -10,7 +10,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
@ -39,10 +42,12 @@ public class HttpDownloadConnection implements Transferable {
private int mStatus = Transferable.STATUS_UNKNOWN; private int mStatus = Transferable.STATUS_UNKNOWN;
private boolean acceptedAutomatically = false; private boolean acceptedAutomatically = false;
private int mProgress = 0; private int mProgress = 0;
private boolean mUseTor = false;
public HttpDownloadConnection(HttpConnectionManager manager) { public HttpDownloadConnection(HttpConnectionManager manager) {
this.mHttpConnectionManager = manager; this.mHttpConnectionManager = manager;
this.mXmppConnectionService = manager.getXmppConnectionService(); this.mXmppConnectionService = manager.getXmppConnectionService();
this.mUseTor = mXmppConnectionService.useTorToConnect();
} }
@Override @Override
@ -191,8 +196,15 @@ public class HttpDownloadConnection implements Transferable {
try { try {
Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive)); Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive));
changeStatus(STATUS_CHECKING); 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"); 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()); connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName());
if (connection instanceof HttpsURLConnection) { if (connection instanceof HttpsURLConnection) {
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
@ -245,7 +257,12 @@ public class HttpDownloadConnection implements Transferable {
PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid()); PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid());
try { try {
wakeLock.acquire(); 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) { if (connection instanceof HttpsURLConnection) {
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
} }

View file

@ -12,7 +12,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL; import java.net.URL;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
@ -46,6 +49,7 @@ public class HttpUploadConnection implements Transferable {
private String mime; private String mime;
private URL mGetUrl; private URL mGetUrl;
private URL mPutUrl; private URL mPutUrl;
private boolean mUseTor = false;
private byte[] key = null; private byte[] key = null;
@ -56,6 +60,7 @@ public class HttpUploadConnection implements Transferable {
public HttpUploadConnection(HttpConnectionManager httpConnectionManager) { public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
this.mHttpConnectionManager = httpConnectionManager; this.mHttpConnectionManager = httpConnectionManager;
this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
this.mUseTor = mXmppConnectionService.useTorToConnect();
} }
@Override @Override
@ -158,7 +163,11 @@ public class HttpUploadConnection implements Transferable {
try { try {
wakeLock.acquire(); wakeLock.acquire();
Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()); Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
if (mUseTor) {
connection = (HttpURLConnection) mPutUrl.openConnection(mHttpConnectionManager.getProxy());
} else {
connection = (HttpURLConnection) mPutUrl.openConnection(); connection = (HttpURLConnection) mPutUrl.openConnection();
}
if (connection instanceof HttpsURLConnection) { if (connection instanceof HttpsURLConnection) {
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
} }

View file

@ -43,7 +43,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null; private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history"; 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 " private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -124,7 +124,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Account.DISPLAY_NAME + " TEXT, " + Account.DISPLAY_NAME + " TEXT, "
+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS + Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
+ " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
+ " TEXT)"); + " TEXT, " + Account.HOSTNAME + " TEXT, " + Account.PORT + " NUMBER DEFAULT 5222)");
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, "
@ -307,6 +307,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 19 && newVersion >= 19) { 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 /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization. * depends on account de-serialization.
*/ */

View file

@ -2106,8 +2106,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
public void createContact(Contact contact) { public void createContact(Contact contact) {
SharedPreferences sharedPref = getPreferences(); boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true);
boolean autoGrant = sharedPref.getBoolean("grant_new_contacts", true);
if (autoGrant) { if (autoGrant) {
contact.setOption(Contact.Options.PREEMPTIVE_GRANT); contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
contact.setOption(Contact.Options.ASKING); contact.setOption(Contact.Options.ASKING);
@ -2534,10 +2533,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
.getDefaultSharedPreferences(getApplicationContext()); .getDefaultSharedPreferences(getApplicationContext());
} }
public boolean forceEncryption() {
return getPreferences().getBoolean("force_encryption", false);
}
public boolean confirmMessages() { public boolean confirmMessages() {
return getPreferences().getBoolean("confirm_messages", true); return getPreferences().getBoolean("confirm_messages", true);
} }
@ -2554,6 +2549,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return getPreferences().getBoolean("indicate_received", false); return getPreferences().getBoolean("indicate_received", false);
} }
public boolean useTorToConnect() {
return getPreferences().getBoolean("use_tor", false);
}
public int unreadCount() { public int unreadCount() {
int count = 0; int count = 0;
for (Conversation conversation : getConversations()) { for (Conversation conversation : getConversations()) {

View file

@ -82,10 +82,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private ImageButton mRegenerateAxolotlKeyButton; private ImageButton mRegenerateAxolotlKeyButton;
private LinearLayout keys; private LinearLayout keys;
private LinearLayout keysCard; private LinearLayout keysCard;
private LinearLayout mNamePort;
private EditText mHostname;
private EditText mPort;
private AlertDialog mCaptchaDialog = null; private AlertDialog mCaptchaDialog = null;
private Jid jidToEdit; private Jid jidToEdit;
private boolean mInitMode = false; private boolean mInitMode = false;
private boolean mUseTor = false;
private Account mAccount; private Account mAccount;
private String messageFingerprint; private String messageFingerprint;
@ -136,6 +140,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} }
final String password = mPassword.getText().toString(); final String password = mPassword.getText().toString();
final String passwordConfirm = mPasswordConfirm.getText().toString(); final String passwordConfirm = mPasswordConfirm.getText().toString();
final String hostname = mHostname.getText().toString();
final String port = mPort.getText().toString();
if (registerNewAccount) { if (registerNewAccount) {
if (!password.equals(passwordConfirm)) { if (!password.equals(passwordConfirm)) {
mPasswordConfirm.setError(getString(R.string.passwords_do_not_match)); mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
@ -149,6 +155,25 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
mPasswordConfirm.setError(null); mPasswordConfirm.setError(null);
mAccount.setPassword(password); mAccount.setPassword(password);
mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); 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); xmppConnectionService.updateAccount(mAccount);
} else { } else {
if (xmppConnectionService.findAccountByJid(jid) != null) { if (xmppConnectionService.findAccountByJid(jid) != null) {
@ -319,7 +344,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
unmodified = this.mAccount.getJid().toBareJid().toString(); unmodified = this.mAccount.getJid().toBareJid().toString();
} }
return !unmodified.equals(this.mAccountJid.getText().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 @Override
@ -368,6 +395,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_axolotl_key); this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_axolotl_key);
this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card); this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
this.keys = (LinearLayout) findViewById(R.id.other_device_keys); 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.mSaveButton = (Button) findViewById(R.id.save_button);
this.mCancelButton = (Button) findViewById(R.id.cancel_button); this.mCancelButton = (Button) findViewById(R.id.cancel_button);
this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener); 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 @Override
@ -529,6 +564,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString()); this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
} }
this.mPassword.setText(this.mAccount.getPassword()); 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) { if (!mInitMode) {
this.mAvatar.setVisibility(View.VISIBLE); this.mAvatar.setVisibility(View.VISIBLE);

View file

@ -160,6 +160,8 @@ public class SettingsActivity extends XmppActivity implements
} else if (name.equals("dont_trust_system_cas")) { } else if (name.equals("dont_trust_system_cas")) {
xmppConnectionService.updateMemorizingTrustmanager(); xmppConnectionService.updateMemorizingTrustmanager();
reconnectAccounts(); reconnectAccounts();
} else if (name.equals("use_tor")) {
reconnectAccounts();
} }
} }

View file

@ -27,6 +27,7 @@ import java.net.ConnectException;
import java.net.IDN; import java.net.IDN;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.net.URL; import java.net.URL;
@ -233,16 +234,23 @@ public class XmppConnection implements Runnable {
tagReader = new XmlReader(wakeLock); tagReader = new XmlReader(wakeLock);
tagWriter = new TagWriter(); tagWriter = new TagWriter();
this.changeStatus(Account.State.CONNECTING); 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())) { if (DNSHelper.isIp(account.getServer().toString())) {
socket = new Socket(); socket = useTor ? new Socket(TOR_PROXY) : new Socket();
try { try {
socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
} catch (IOException e) { } catch (IOException e) {
throw new UnknownHostException(); throw new UnknownHostException();
} }
} else {
final ArrayList<Parcelable> values;
if (useTor) {
values = account.getHostnamePortBundles();
} else { } else {
final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService); final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService);
final ArrayList<Parcelable> values = result.getParcelableArrayList("values"); values = result.getParcelableArrayList("values");
}
int i = 0; int i = 0;
boolean socketError = true; boolean socketError = true;
while (socketError && values.size() > i) { while (socketError && values.size() > i) {
@ -269,11 +277,11 @@ public class XmppConnection implements Runnable {
+ ": using values from dns " + ": using values from dns "
+ srvRecordServer + ":" + srvRecordPort); + srvRecordServer + ":" + srvRecordPort);
} }
socket = new Socket(); socket = useTor ? new Socket(TOR_PROXY) : new Socket();
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
socketError = false; socketError = false;
} catch (final Throwable e) { } 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++; i++;
} }
} }

View file

@ -7,7 +7,9 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -59,7 +61,9 @@ public class JingleSocks5Transport extends JingleTransport {
@Override @Override
public void run() { public void run() {
try { 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()); SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort());
socket.connect(address,Config.SOCKET_TIMEOUT * 1000); socket.connect(address,Config.SOCKET_TIMEOUT * 1000);
inputStream = socket.getInputStream(); inputStream = socket.getInputStream();

View file

@ -78,6 +78,58 @@
android:textColorHint="@color/black54" android:textColorHint="@color/black54"
android:textSize="?attr/TextSizeBody" /> android:textSize="?attr/TextSizeBody" />
<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 <CheckBox
android:id="@+id/account_register_new" android:id="@+id/account_register_new"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -31,4 +31,9 @@
<item android:id="@+id/action_clear_devices" <item android:id="@+id/action_clear_devices"
android:title="@string/clear_other_devices" android:title="@string/clear_other_devices"
android:showAsAction="never"/> android:showAsAction="never"/>
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_settings"/>
</menu> </menu>

View file

@ -540,4 +540,12 @@
<string name="error_fetching_omemo_key">Error fetching OMEMO key!</string> <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="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="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> </resources>

View file

@ -147,6 +147,13 @@
android:summary="@string/pref_remove_trusted_certificates_summary" android:summary="@string/pref_remove_trusted_certificates_summary"
android:title="@string/pref_remove_trusted_certificates_title"/> android:title="@string/pref_remove_trusted_certificates_title"/>
</PreferenceCategory> </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"> <PreferenceCategory android:title="@string/pref_input_options">
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"