Permalink
Browse files

Add support for SOCKS proxy.

  • Loading branch information...
jonas-lundqvist committed Oct 23, 2015
1 parent 2567f8c commit d85562bd1a6b43091903bc15e5bbbd56db928d9c
@@ -0,0 +1,13 @@
package com.fsck.k9.mail;
public class ProxySettings {
public boolean enabled;
public String host;
public int port;
public ProxySettings(boolean enabled, String host, int port) {
this.enabled = enabled;
this.host = host;
this.port = port;
}
}
@@ -19,10 +19,12 @@
// RFC 1047
protected static final int SOCKET_READ_TIMEOUT = 300000;
public synchronized static Transport getInstance(Context context, StoreConfig storeConfig) throws MessagingException {
public synchronized static Transport getInstance(Context context,
StoreConfig storeConfig,
ProxySettings proxy) throws MessagingException {
String uri = storeConfig.getTransportUri();
if (uri.startsWith("smtp")) {
return new SmtpTransport(storeConfig, new DefaultTrustedSocketFactory(context));
return new SmtpTransport(storeConfig, new DefaultTrustedSocketFactory(context, proxy));
} else if (uri.startsWith("webdav")) {
return new WebDavTransport(storeConfig);
} else {
@@ -21,7 +21,11 @@
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.net.InetSocketAddress;
import java.net.Proxy;
import com.fsck.k9.mail.ProxySettings;
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
import static com.fsck.k9.mail.store.RemoteStore.SOCKET_CONNECT_TIMEOUT;
/**
@@ -85,6 +89,8 @@
"SSLv3"
};
private ProxySettings proxySettings;
static {
String[] enabledCiphers = null;
String[] supportedProtocols = null;
@@ -114,8 +120,9 @@
reorder(supportedProtocols, ORDERED_KNOWN_PROTOCOLS, BLACKLISTED_PROTOCOLS);
}
public DefaultTrustedSocketFactory(Context context) {
public DefaultTrustedSocketFactory(Context context, ProxySettings proxySettings) {
this.context = context;
this.proxySettings = proxySettings;
}
protected static String[] reorder(String[] enabled, String[] known, String[] blacklisted) {
@@ -160,7 +167,14 @@ public Socket createSocket(Socket socket, String host, int port, String clientCe
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
Socket trustedSocket;
if (socket == null) {
trustedSocket = socketFactory.createSocket();
if (proxySettings.enabled) {
InetSocketAddress proxyAddr = new InetSocketAddress(proxySettings.host, proxySettings.port);
Socket underlying = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr));
underlying.connect(new InetSocketAddress(host, port), SOCKET_CONNECT_TIMEOUT);
trustedSocket = socketFactory.createSocket(underlying, proxySettings.host, proxySettings.port, true);
} else {
trustedSocket = socketFactory.createSocket();
}
} else {
trustedSocket = socketFactory.createSocket(socket, host, port, true);
}
@@ -169,6 +183,10 @@ public Socket createSocket(Socket socket, String host, int port, String clientCe
hardenSocket(sslSocket);
setSniHost(socketFactory, sslSocket, host);
if (proxySettings.enabled == false && socket == null) {
trustedSocket.connect(new InetSocketAddress(host, port), SOCKET_CONNECT_TIMEOUT);
}
return trustedSocket;
}
@@ -4,6 +4,7 @@
import android.net.ConnectivityManager;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ProxySettings;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.ServerSettings.Type;
import com.fsck.k9.mail.Store;
@@ -38,7 +39,8 @@ public RemoteStore(StoreConfig storeConfig, TrustedSocketFactory trustedSocketFa
* Get an instance of a remote mail store.
*/
public synchronized static Store getInstance(Context context,
StoreConfig storeConfig) throws MessagingException {
StoreConfig storeConfig,
ProxySettings proxySettings) throws MessagingException {
String uri = storeConfig.getStoreUri();
if (uri.startsWith("local")) {
@@ -49,11 +51,11 @@ public synchronized static Store getInstance(Context context,
if (store == null) {
if (uri.startsWith("imap")) {
store = new ImapStore(storeConfig,
new DefaultTrustedSocketFactory(context),
new DefaultTrustedSocketFactory(context, proxySettings),
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
} else if (uri.startsWith("pop3")) {
store = new Pop3Store(storeConfig,
new DefaultTrustedSocketFactory(context));
new DefaultTrustedSocketFactory(context, proxySettings));
} else if (uri.startsWith("webdav")) {
store = new WebDavStore(storeConfig);
}
@@ -574,8 +574,9 @@ private static Socket connect(ImapSettings settings, TrustedSocketFactory socket
settings.getClientCertificateAlias());
} else {
socket = new Socket();
socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
}
socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
// Successfully connected to the server; don't try any other addresses
return socket;
} catch (IOException e) {
@@ -310,9 +310,9 @@ public synchronized void open(int mode) throws MessagingException {
mSocket = mTrustedSocketFactory.createSocket(null, mHost, mPort, mClientCertificateAlias);
} else {
mSocket = new Socket();
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
}
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
@@ -215,7 +215,6 @@ public void open() throws MessagingException {
SocketAddress socketAddress = new InetSocketAddress(addresses[i], mPort);
if (mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
mSocket = mTrustedSocketFactory.createSocket(null, mHost, mPort, mClientCertificateAlias);
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
secureConnection = true;
} else {
mSocket = new Socket();
@@ -28,6 +28,7 @@
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.NetworkType;
import com.fsck.k9.mail.ProxySettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Folder.FolderClass;
import com.fsck.k9.mail.filter.Base64;
@@ -1275,7 +1276,12 @@ public LocalStore getLocalStore() throws MessagingException {
}
public Store getRemoteStore() throws MessagingException {
return RemoteStore.getInstance(K9.app, this);
return RemoteStore.getInstance(K9.app,
this,
new ProxySettings(
K9.isSocksProxyEnabled(),
K9.getSocksProxyHost(),
K9.getSocksProxyPort()));
}
// It'd be great if this actually went into the store implementation
@@ -260,6 +260,9 @@
private static boolean sMessageViewCopyActionVisible = false;
private static boolean sMessageViewSpamActionVisible = false;
private static boolean sUseSocksProxy = false;
private static String sSocksProxyHost = "";
private static int sSocksProxyPort = 0;
/**
* @see #areDatabasesUpToDate()
@@ -506,6 +509,10 @@ public static void save(SharedPreferences.Editor editor) {
editor.putBoolean("messageViewCopyActionVisible", sMessageViewCopyActionVisible);
editor.putBoolean("messageViewSpamActionVisible", sMessageViewSpamActionVisible);
editor.putBoolean("useSocksProxy", sUseSocksProxy);
editor.putString("socksProxyHost", sSocksProxyHost);
editor.putInt("useSocksPort", sSocksProxyPort);
fontSizes.save(editor);
}
@@ -744,7 +751,7 @@ public static void loadPrefs(Preferences prefs) {
sSplitViewMode = SplitViewMode.valueOf(splitViewMode);
}
mAttachmentDefaultPath = sprefs.getString("attachmentdefaultpath", Environment.getExternalStorageDirectory().toString());
mAttachmentDefaultPath = sprefs.getString("attachmentdefaultpath", Environment.getExternalStorageDirectory().toString());
sUseBackgroundAsUnreadIndicator = sprefs.getBoolean("useBackgroundAsUnreadIndicator", true);
sThreadedViewEnabled = sprefs.getBoolean("threadedView", true);
fontSizes.load(sprefs);
@@ -765,6 +772,10 @@ public static void loadPrefs(Preferences prefs) {
sMessageViewCopyActionVisible = sprefs.getBoolean("messageViewCopyActionVisible", false);
sMessageViewSpamActionVisible = sprefs.getBoolean("messageViewSpamActionVisible", false);
sUseSocksProxy = sprefs.getBoolean("useSocksProxy", false);
sSocksProxyHost = sprefs.getString("socksProxyHost", "127.0.0.1");
sSocksProxyPort = sprefs.getInt("socksProxyPort", 12345);
K9.setK9Language(sprefs.getString("language", ""));
@@ -1343,6 +1354,24 @@ public static void setMessageViewSpamActionVisible(boolean visible) {
sMessageViewSpamActionVisible = visible;
}
public static boolean isSocksProxyEnabled() { return sUseSocksProxy; }
public static void setUseSocksProxy(boolean useSocksProxy) {
sUseSocksProxy = useSocksProxy;
}
public static String getSocksProxyHost() { return sSocksProxyHost; }
public static void setSocksProxyHost(String socksProxyHost) {
sSocksProxyHost = socksProxyHost;
}
public static int getSocksProxyPort() { return sSocksProxyPort; }
public static void setSocksProxyPort(int socksProxyPort) {
sSocksProxyPort = socksProxyPort;
}
/**
* Check if we already know whether all databases are using the current database schema.
*
@@ -25,6 +25,7 @@
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ProxySettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.webdav.WebDavStore;
@@ -476,7 +477,12 @@ private void checkOutgoing() throws MessagingException {
if (!(account.getRemoteStore() instanceof WebDavStore)) {
publishProgress(R.string.account_setup_check_settings_check_outgoing_msg);
}
Transport transport = Transport.getInstance(K9.app, account);
Transport transport = Transport.getInstance(K9.app,
account,
new ProxySettings(
K9.isSocksProxyEnabled(),
K9.getSocksProxyHost(),
K9.getSocksProxyPort()));
transport.close();
transport.open();
transport.close();
@@ -14,6 +14,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
@@ -100,6 +101,10 @@
private static final String PREFERENCE_FOLDERLIST_WRAP_NAME = "folderlist_wrap_folder_name";
private static final String PREFERENCE_SPLITVIEW_MODE = "splitview_mode";
private static final String PREFERENCE_SOCKS_PROXY = "socks_proxy";
private static final String PREFERENCE_SOCKS_PROXY_HOST = "socks_proxy_host";
private static final String PREFERENCE_SOCKS_PROXY_PORT = "socks_proxy_port";
private static final int ACTIVITY_CHOOSE_FOLDER = 1;
// Named indices for the mVisibleRefileActions field
@@ -156,6 +161,9 @@
private CheckBoxPreference mThreadedView;
private ListPreference mSplitViewMode;
private CheckBoxPreference mUseSocksProxy;
private EditTextPreference mSocksProxyHost;
private EditTextPreference mSocksProxyPort;
public static void actionPrefs(Context context) {
Intent i = new Intent(context, Prefs.class);
@@ -422,6 +430,15 @@ public void onCancel() {
mSplitViewMode = (ListPreference) findPreference(PREFERENCE_SPLITVIEW_MODE);
initListPreference(mSplitViewMode, K9.getSplitViewMode().name(),
mSplitViewMode.getEntries(), mSplitViewMode.getEntryValues());
mUseSocksProxy = (CheckBoxPreference)findPreference(PREFERENCE_SOCKS_PROXY);
mUseSocksProxy.setChecked(K9.isSocksProxyEnabled());
mSocksProxyHost = (EditTextPreference)findPreference(PREFERENCE_SOCKS_PROXY_HOST);
mSocksProxyHost.setText(K9.getSocksProxyHost());
mSocksProxyPort = (EditTextPreference)findPreference(PREFERENCE_SOCKS_PROXY_PORT);
mSocksProxyPort.setText(K9.getSocksProxyPort()+"");
}
private static String themeIdToName(K9.Theme theme) {
@@ -522,6 +539,10 @@ private void saveSettings() {
K9.setHideUserAgent(mHideUserAgent.isChecked());
K9.setHideTimeZone(mHideTimeZone.isChecked());
K9.setUseSocksProxy(mUseSocksProxy.isChecked());
K9.setSocksProxyHost(mSocksProxyHost.getText());
K9.setSocksProxyPort(Integer.parseInt(mSocksProxyPort.getText()));
Editor editor = preferences.edit();
K9.save(editor);
editor.commit();
@@ -45,6 +45,7 @@
import com.fsck.k9.activity.setup.AccountSetupCheckSettings.CheckDirection;
import com.fsck.k9.cache.EmailProviderCache;
import com.fsck.k9.mail.CertificateValidationException;
import com.fsck.k9.mail.ProxySettings;
import com.fsck.k9.mail.power.TracingPowerManager;
import com.fsck.k9.mail.power.TracingPowerManager.TracingWakeLock;
import com.fsck.k9.mail.Address;
@@ -3092,7 +3093,12 @@ public void sendPendingMessagesSynchronous(final Account account) {
if (K9.DEBUG)
Log.i(K9.LOG_TAG, "Scanning folder '" + account.getOutboxFolderName() + "' (" + ((LocalFolder)localFolder).getId() + ") for messages to send");
Transport transport = Transport.getInstance(K9.app, account);
Transport transport = Transport.getInstance(K9.app,
account,
new ProxySettings(
K9.isSocksProxyEnabled(),
K9.getSocksProxyHost(),
K9.getSocksProxyPort()));
for (Message message : localMessages) {
if (message.isSet(Flag.DELETED)) {
message.destroy();
@@ -272,6 +272,15 @@
s.put("confirmDiscardMessage", Settings.versions(
new V(40, new BooleanSetting(true))
));
s.put("useSocksProxy", Settings.versions(
new V(41, new BooleanSetting(true))
));
s.put("socksProxyHost", Settings.versions(
new V(41, new StringSetting("127.0.0.1"))
));
s.put("socksProxyPort", Settings.versions(
new V(41, new IntegerRangeSetting(0, 65535, 12345))
));
SETTINGS = Collections.unmodifiableMap(s);
@@ -35,7 +35,7 @@
*
* @see SettingsExporter
*/
public static final int VERSION = 40;
public static final int VERSION = 41;
public static Map<String, Object> validate(int version, Map<String,
TreeMap<Integer, SettingsDescription>> settings,
@@ -826,6 +826,13 @@ Please submit bug reports, contribute new features and ask questions at
<string name="background_ops_always">Always</string>
<string name="background_ops_auto_sync_only">When \'Auto-sync\' is checked</string>
<string name="socks_proxy_label">SOCKS Proxy</string>
<string name="socks_proxy_description">Enable SOCKS Proxy</string>
<string name="socks_proxy_host_label">SOCKS Host</string>
<string name="socks_proxy_host_description">SOCKS Host</string>
<string name="socks_proxy_port_label">SOCKS Port</string>
<string name="socks_proxy_port_description">SOCKS Port number</string>
<string name="batch_select_all">Select all</string>
<string name="account_setup_push_limit_label">Max folders to check with push</string>
Oops, something went wrong.

0 comments on commit d85562b

Please sign in to comment.