mirror of
https://github.com/moparisthebest/k-9
synced 2025-01-10 05:08:18 -05:00
Update EasStore for the new URI scheme. Implement the DeviceID for EAS.
This commit is contained in:
parent
cdba29b002
commit
41b292ee9d
@ -5,6 +5,7 @@ import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
|
||||
@ -14,6 +15,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
@ -21,6 +23,7 @@ import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.Time;
|
||||
import android.util.Log;
|
||||
|
||||
@ -54,7 +57,7 @@ public class K9 extends Application {
|
||||
void initializeComponent(K9 application);
|
||||
}
|
||||
|
||||
public static Application app = null;
|
||||
public static K9 app = null;
|
||||
public static File tempDirectory;
|
||||
public static final String LOG_TAG = "k9";
|
||||
|
||||
@ -76,6 +79,7 @@ public class K9 extends Application {
|
||||
private static final FontSizes fontSizes = new FontSizes();
|
||||
|
||||
private static BACKGROUND_OPS backgroundOps = BACKGROUND_OPS.WHEN_CHECKED;
|
||||
|
||||
/**
|
||||
* Some log messages can be sent to a file, so that the logs
|
||||
* can be read using unprivileged access (eg. Terminal Emulator)
|
||||
@ -91,7 +95,6 @@ public class K9 extends Application {
|
||||
**/
|
||||
public static boolean DEVELOPER_MODE = true;
|
||||
|
||||
|
||||
/**
|
||||
* If this is enabled there will be additional logging information sent to
|
||||
* Log.d, including protocol dumps.
|
||||
@ -103,33 +106,26 @@ public class K9 extends Application {
|
||||
* Should K-9 log the conversation it has over the wire with
|
||||
* SMTP servers?
|
||||
*/
|
||||
|
||||
public static boolean DEBUG_PROTOCOL_SMTP = true;
|
||||
|
||||
/**
|
||||
* Should K-9 log the conversation it has over the wire with
|
||||
* IMAP servers?
|
||||
*/
|
||||
|
||||
public static boolean DEBUG_PROTOCOL_IMAP = true;
|
||||
|
||||
|
||||
/**
|
||||
* Should K-9 log the conversation it has over the wire with
|
||||
* POP3 servers?
|
||||
*/
|
||||
|
||||
public static boolean DEBUG_PROTOCOL_POP3 = true;
|
||||
|
||||
/**
|
||||
* Should K-9 log the conversation it has over the wire with
|
||||
* WebDAV servers?
|
||||
*/
|
||||
|
||||
public static boolean DEBUG_PROTOCOL_WEBDAV = true;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If this is enabled than logging that normally hides sensitive information
|
||||
* like passwords will show that information.
|
||||
@ -144,18 +140,15 @@ public class K9 extends Application {
|
||||
public static String ERROR_FOLDER_NAME = "K9mail-errors";
|
||||
|
||||
private static boolean mAnimations = true;
|
||||
|
||||
private static boolean mConfirmDelete = false;
|
||||
private static boolean mConfirmDeleteStarred = false;
|
||||
private static boolean mConfirmSpam = false;
|
||||
private static boolean mConfirmMarkAllAsRead = true;
|
||||
private static boolean mKeyguardPrivacy = false;
|
||||
|
||||
private static boolean mMessageListStars = true;
|
||||
private static boolean mMessageListCheckboxes = false;
|
||||
private static boolean mMessageListTouchable = false;
|
||||
private static int mMessageListPreviewLines = 2;
|
||||
|
||||
private static boolean mShowCorrespondentNames = true;
|
||||
private static boolean mShowContactName = false;
|
||||
private static boolean mChangeContactNameColor = false;
|
||||
@ -163,7 +156,6 @@ public class K9 extends Application {
|
||||
private static boolean mMessageViewFixedWidthFont = false;
|
||||
private static boolean mMessageViewReturnToList = false;
|
||||
private static boolean mMessageViewShowNext = false;
|
||||
|
||||
private static boolean mGesturesEnabled = true;
|
||||
private static boolean mUseVolumeKeysForNavigation = false;
|
||||
private static boolean mUseVolumeKeysForListNavigation = false;
|
||||
@ -179,11 +171,9 @@ public class K9 extends Application {
|
||||
private static String mQuietTimeEnds = null;
|
||||
private static boolean compactLayouts = false;
|
||||
private static String mAttachmentDefaultPath = "";
|
||||
|
||||
|
||||
private static boolean useGalleryBugWorkaround = false;
|
||||
private static boolean galleryBuggy;
|
||||
|
||||
private static String mDeviceId = null;
|
||||
|
||||
/**
|
||||
* The MIME type(s) of attachments we're willing to view.
|
||||
@ -238,10 +228,10 @@ public class K9 extends Application {
|
||||
public static final int MAX_ATTACHMENT_DOWNLOAD_SIZE = (128 * 1024 * 1024);
|
||||
|
||||
|
||||
/* How many times should K-9 try to deliver a message before giving up
|
||||
/**
|
||||
* How many times should K-9 try to deliver a message before giving up
|
||||
* until the app is killed and restarted
|
||||
*/
|
||||
|
||||
public static int MAX_SEND_ATTEMPTS = 5;
|
||||
|
||||
/**
|
||||
@ -262,25 +252,18 @@ public class K9 extends Application {
|
||||
*/
|
||||
public static final int NOTIFICATION_LED_ON_TIME = 500;
|
||||
public static final int NOTIFICATION_LED_OFF_TIME = 2000;
|
||||
|
||||
public static final boolean NOTIFICATION_LED_WHILE_SYNCING = false;
|
||||
public static final int NOTIFICATION_LED_FAST_ON_TIME = 100;
|
||||
public static final int NOTIFICATION_LED_FAST_OFF_TIME = 100;
|
||||
|
||||
|
||||
public static final int NOTIFICATION_LED_BLINK_SLOW = 0;
|
||||
public static final int NOTIFICATION_LED_BLINK_FAST = 1;
|
||||
|
||||
|
||||
|
||||
public static final int NOTIFICATION_LED_SENDING_FAILURE_COLOR = 0xffff0000;
|
||||
|
||||
// Must not conflict with an account number
|
||||
public static final int FETCHING_EMAIL_NOTIFICATION = -5000;
|
||||
public static final int SEND_FAILED_NOTIFICATION = -1500;
|
||||
public static final int FETCHING_EMAIL_NOTIFICATION = -5000;
|
||||
public static final int SEND_FAILED_NOTIFICATION = -1500;
|
||||
public static final int CONNECTIVITY_ID = -3;
|
||||
|
||||
|
||||
public static class Intents {
|
||||
|
||||
public static class EmailReceived {
|
||||
@ -317,7 +300,6 @@ public class K9 extends Application {
|
||||
int acctLength = Preferences.getPreferences(context).getAvailableAccounts().size();
|
||||
|
||||
setServicesEnabled(context, acctLength > 0, null);
|
||||
|
||||
}
|
||||
|
||||
private static void setServicesEnabled(Context context, boolean enabled, Integer wakeLockId) {
|
||||
@ -356,7 +338,6 @@ public class K9 extends Application {
|
||||
*/
|
||||
MailService.actionReset(context, wakeLockId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -414,7 +395,6 @@ public class K9 extends Application {
|
||||
editor.putBoolean("quietTimeEnabled", mQuietTimeEnabled);
|
||||
editor.putString("quietTimeStarts", mQuietTimeStarts);
|
||||
editor.putString("quietTimeEnds", mQuietTimeEnds);
|
||||
|
||||
editor.putBoolean("startIntegratedInbox", mStartIntegratedInbox);
|
||||
editor.putBoolean("measureAccounts", mMeasureAccounts);
|
||||
editor.putBoolean("countSearchMessages", mCountSearchMessages);
|
||||
@ -423,7 +403,6 @@ public class K9 extends Application {
|
||||
editor.putBoolean("messageListCheckboxes", mMessageListCheckboxes);
|
||||
editor.putBoolean("messageListTouchable", mMessageListTouchable);
|
||||
editor.putInt("messageListPreviewLines", mMessageListPreviewLines);
|
||||
|
||||
editor.putBoolean("showCorrespondentNames", mShowCorrespondentNames);
|
||||
editor.putBoolean("showContactName", mShowContactName);
|
||||
editor.putBoolean("changeRegisteredNameColor", mChangeContactNameColor);
|
||||
@ -431,20 +410,17 @@ public class K9 extends Application {
|
||||
editor.putBoolean("messageViewFixedWidthFont", mMessageViewFixedWidthFont);
|
||||
editor.putBoolean("messageViewReturnToList", mMessageViewReturnToList);
|
||||
editor.putBoolean("messageViewShowNext", mMessageViewShowNext);
|
||||
|
||||
editor.putString("language", language);
|
||||
editor.putInt("theme", theme);
|
||||
editor.putBoolean("useGalleryBugWorkaround", useGalleryBugWorkaround);
|
||||
|
||||
editor.putBoolean("confirmDelete", mConfirmDelete);
|
||||
editor.putBoolean("confirmDeleteStarred", mConfirmDeleteStarred);
|
||||
editor.putBoolean("confirmSpam", mConfirmSpam);
|
||||
editor.putBoolean("confirmMarkAllAsRead", mConfirmMarkAllAsRead);
|
||||
|
||||
editor.putBoolean("keyguardPrivacy", mKeyguardPrivacy);
|
||||
|
||||
editor.putBoolean("compactLayouts", compactLayouts);
|
||||
editor.putString("attachmentdefaultpath", mAttachmentDefaultPath);
|
||||
editor.putString("deviceId", mDeviceId);
|
||||
fontSizes.save(editor);
|
||||
}
|
||||
|
||||
@ -454,7 +430,6 @@ public class K9 extends Application {
|
||||
super.onCreate();
|
||||
app = this;
|
||||
|
||||
|
||||
galleryBuggy = checkForBuggyGallery();
|
||||
|
||||
Preferences prefs = Preferences.getPreferences(this);
|
||||
@ -469,7 +444,6 @@ public class K9 extends Application {
|
||||
/*
|
||||
* Enable background sync of messages
|
||||
*/
|
||||
|
||||
setServicesEnabled(this);
|
||||
registerReceivers();
|
||||
|
||||
@ -547,14 +521,11 @@ public class K9 extends Application {
|
||||
mMessageListCheckboxes = sprefs.getBoolean("messageListCheckboxes", false);
|
||||
mMessageListTouchable = sprefs.getBoolean("messageListTouchable", false);
|
||||
mMessageListPreviewLines = sprefs.getInt("messageListPreviewLines", 2);
|
||||
|
||||
mMobileOptimizedLayout = sprefs.getBoolean("mobileOptimizedLayout", false);
|
||||
mZoomControlsEnabled = sprefs.getBoolean("zoomControlsEnabled", false);
|
||||
|
||||
mQuietTimeEnabled = sprefs.getBoolean("quietTimeEnabled", false);
|
||||
mQuietTimeStarts = sprefs.getString("quietTimeStarts", "21:00");
|
||||
mQuietTimeEnds = sprefs.getString("quietTimeEnds", "7:00");
|
||||
|
||||
mShowCorrespondentNames = sprefs.getBoolean("showCorrespondentNames", true);
|
||||
mShowContactName = sprefs.getBoolean("showContactName", false);
|
||||
mChangeContactNameColor = sprefs.getBoolean("changeRegisteredNameColor", false);
|
||||
@ -562,19 +533,15 @@ public class K9 extends Application {
|
||||
mMessageViewFixedWidthFont = sprefs.getBoolean("messageViewFixedWidthFont", false);
|
||||
mMessageViewReturnToList = sprefs.getBoolean("messageViewReturnToList", false);
|
||||
mMessageViewShowNext = sprefs.getBoolean("messageViewShowNext", false);
|
||||
|
||||
useGalleryBugWorkaround = sprefs.getBoolean("useGalleryBugWorkaround", K9.isGalleryBuggy());
|
||||
|
||||
mConfirmDelete = sprefs.getBoolean("confirmDelete", false);
|
||||
mConfirmDeleteStarred = sprefs.getBoolean("confirmDeleteStarred", false);
|
||||
mConfirmSpam = sprefs.getBoolean("confirmSpam", false);
|
||||
mConfirmMarkAllAsRead = sprefs.getBoolean("confirmMarkAllAsRead", true);
|
||||
|
||||
|
||||
mKeyguardPrivacy = sprefs.getBoolean("keyguardPrivacy", false);
|
||||
|
||||
compactLayouts = sprefs.getBoolean("compactLayouts", false);
|
||||
mAttachmentDefaultPath = sprefs.getString("attachmentdefaultpath", Environment.getExternalStorageDirectory().toString());
|
||||
mDeviceId = sprefs.getString("deviceId", null);
|
||||
fontSizes.load(sprefs);
|
||||
|
||||
try {
|
||||
@ -601,10 +568,8 @@ public class K9 extends Application {
|
||||
// Discard , as it means we're not running on a device with strict mode
|
||||
Log.v(K9.LOG_TAG, "Failed to turn on strict mode", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* since Android invokes Application.onCreate() only after invoking all
|
||||
* other components' onCreate(), here is a way to notify interested
|
||||
@ -738,7 +703,6 @@ public class K9 extends Application {
|
||||
mQuietTimeEnds = quietTimeEnds;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isQuietTime() {
|
||||
if (!mQuietTimeEnabled) {
|
||||
return false;
|
||||
@ -760,7 +724,6 @@ public class K9 extends Application {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 21:00 - 05:00 means we want to be quiet if it's after 9 or before 5
|
||||
if (quietStarts > quietEnds) {
|
||||
// if it's 22:00 or 03:00 but not 8:00
|
||||
@ -781,8 +744,6 @@ public class K9 extends Application {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static boolean startIntegratedInbox() {
|
||||
return mStartIntegratedInbox;
|
||||
}
|
||||
@ -1016,4 +977,26 @@ public class K9 extends Application {
|
||||
public static void setAttachmentDefaultPath(String attachmentDefaultPath) {
|
||||
K9.mAttachmentDefaultPath = attachmentDefaultPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a uuid that is to identify this application on this device.
|
||||
* The uuid is only generated once, and then persisted.
|
||||
* @return the device Id
|
||||
*/
|
||||
public String getDeviceId() {
|
||||
if (TextUtils.isEmpty(mDeviceId)) {
|
||||
// Remove the dashes from the UUID, so that we have alphanumeric characters only.
|
||||
// This increases the likelyhood of compatability with external systems that may
|
||||
// use the ID.
|
||||
mDeviceId = UUID.randomUUID().toString().replace("-", "");
|
||||
|
||||
// This particular setting is not editted through the settings view, but instead only
|
||||
// set in this method. We need to commit it now to ensure it is persisted correctly.
|
||||
final Preferences preferences = Preferences.getPreferences(this);
|
||||
Editor editor = preferences.getPreferences().edit();
|
||||
editor.putString("deviceId", mDeviceId);
|
||||
editor.commit();
|
||||
}
|
||||
return mDeviceId;
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ public abstract class Store {
|
||||
* @see ImapStore#decodeUri(String)
|
||||
* @see Pop3Store#decodeUri(String)
|
||||
* @see WebDavStore#decodeUri(String)
|
||||
* @see EasStore#decodeUri(String)
|
||||
*/
|
||||
public static ServerSettings decodeStoreUri(String uri) {
|
||||
if (uri.startsWith("imap")) {
|
||||
@ -107,6 +108,8 @@ public abstract class Store {
|
||||
return Pop3Store.decodeUri(uri);
|
||||
} else if (uri.startsWith("webdav")) {
|
||||
return WebDavStore.decodeUri(uri);
|
||||
} else if (uri.startsWith("eas")) {
|
||||
return EasStore.decodeUri(uri);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Not a valid store URI");
|
||||
}
|
||||
@ -123,6 +126,7 @@ public abstract class Store {
|
||||
* @see ImapStore#createUri(ServerSettings)
|
||||
* @see Pop3Store#createUri(ServerSettings)
|
||||
* @see WebDavStore#createUri(ServerSettings)
|
||||
* @see EasStore#createUri(ServerSettings)
|
||||
*/
|
||||
public static String createStoreUri(ServerSettings server) {
|
||||
if (ImapStore.STORE_TYPE.equals(server.type)) {
|
||||
@ -131,6 +135,8 @@ public abstract class Store {
|
||||
return Pop3Store.createUri(server);
|
||||
} else if (WebDavStore.STORE_TYPE.equals(server.type)) {
|
||||
return WebDavStore.createUri(server);
|
||||
} else if (EasStore.STORE_TYPE.equals(server.type)) {
|
||||
return EasStore.createUri(server);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Not a valid store URI");
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
@ -51,6 +50,7 @@ import com.fsck.k9.controller.MessageRetrievalListener;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager;
|
||||
import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
||||
import com.fsck.k9.mail.AuthenticationFailedException;
|
||||
import com.fsck.k9.mail.ConnectionSecurity;
|
||||
import com.fsck.k9.mail.FetchProfile;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.Folder;
|
||||
@ -58,6 +58,7 @@ import com.fsck.k9.mail.Message;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.PushReceiver;
|
||||
import com.fsck.k9.mail.Pusher;
|
||||
import com.fsck.k9.mail.ServerSettings;
|
||||
import com.fsck.k9.mail.Store;
|
||||
import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
@ -89,31 +90,32 @@ public class EasStore extends Store {
|
||||
// we sync the items any "collection" (emails in a folder).
|
||||
private static final String INITIAL_SYNC_KEY = "0";
|
||||
|
||||
static private final String PING_COMMAND = "Ping";
|
||||
static private final String PROVISION_COMMAND = "Provision";
|
||||
private static final String PING_COMMAND = "Ping";
|
||||
private static final String PROVISION_COMMAND = "Provision";
|
||||
|
||||
// Command timeout is the the time allowed for reading data from an open connection before an
|
||||
// IOException is thrown. After a small added allowance, our watch dog alarm goes off (allowing
|
||||
// us to detect a silently dropped connection). The allowance is defined below.
|
||||
static private final int COMMAND_TIMEOUT = 30 * 1000;
|
||||
private static final int COMMAND_TIMEOUT = 30 * 1000;
|
||||
// Connection timeout is the time given to connect to the server before reporting an IOException.
|
||||
static private final int CONNECTION_TIMEOUT = 20 * 1000;
|
||||
private static final int CONNECTION_TIMEOUT = 20 * 1000;
|
||||
|
||||
// This needs to be long enough to send the longest reasonable message, without being so long
|
||||
// as to effectively "hang" sending of mail. The standard 30 second timeout isn't long enough
|
||||
// for pictures and the like. For now, we'll use 15 minutes, in the knowledge that any socket
|
||||
// failure would probably generate an Exception before timing out anyway.
|
||||
public static final int SEND_MAIL_TIMEOUT = 15 * 60 * 1000;
|
||||
private static final int SEND_MAIL_TIMEOUT = 15 * 60 * 1000;
|
||||
|
||||
// MSFT's custom HTTP result code indicating the need to provision.
|
||||
static private final int HTTP_NEED_PROVISIONING = 449;
|
||||
private static final int HTTP_NEED_PROVISIONING = 449;
|
||||
|
||||
// The EAS protocol Provision status for "we implement all of the policies".
|
||||
static private final String PROVISION_STATUS_OK = "1";
|
||||
private static final String PROVISION_STATUS_OK = "1";
|
||||
// The EAS protocol Provision status meaning "we partially implement the policies".
|
||||
static private final String PROVISION_STATUS_PARTIAL = "2";
|
||||
private static final String PROVISION_STATUS_PARTIAL = "2";
|
||||
|
||||
static public final String EAS_12_POLICY_TYPE = "MS-EAS-Provisioning-WBXML";
|
||||
static public final String EAS_2_POLICY_TYPE = "MS-WAP-Provisioning-XML";
|
||||
public static final String EAS_12_POLICY_TYPE = "MS-EAS-Provisioning-WBXML";
|
||||
public static final String EAS_2_POLICY_TYPE = "MS-WAP-Provisioning-XML";
|
||||
|
||||
private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000;
|
||||
private static final int IDLE_FAILURE_COUNT_LIMIT = 10;
|
||||
@ -122,77 +124,178 @@ public class EasStore extends Store {
|
||||
|
||||
// The number of emails to fetch for each request to the server.
|
||||
private static final int EMAIL_WINDOW_SIZE = 10;
|
||||
// The maximum length of the DeviceID parameter used by EAS is 32 characters.
|
||||
private static final int MAX_DEVICE_ID_SIZE = 32;
|
||||
|
||||
public String mProtocolVersion;
|
||||
public Double mProtocolVersionDouble;
|
||||
protected String mDeviceId = null;
|
||||
protected String mDeviceType = "Android";
|
||||
private String mCmdString = null;
|
||||
/**
|
||||
* Decodes a EasStore URI.
|
||||
*
|
||||
* <p>Possible forms:</p>
|
||||
* <pre>
|
||||
* eas://user:password@server:port CONNECTION_SECURITY_NONE
|
||||
* eas+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
* eas+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||
* eas+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||
* eas+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||
* </pre>
|
||||
*/
|
||||
public static ServerSettings decodeUri(String uri) {
|
||||
String host;
|
||||
int port;
|
||||
ConnectionSecurity connectionSecurity;
|
||||
String username = null;
|
||||
String password = null;
|
||||
|
||||
private final URI mUri; /* Stores the Uniform Resource Indicator with all connection info */
|
||||
private String mHost; /* Stores the host name for the server */
|
||||
private String mUsername; /* Stores the username for authentications */
|
||||
private String mPassword; /* Stores the password for authentications */
|
||||
URI easUri;
|
||||
try {
|
||||
easUri = new URI(uri);
|
||||
} catch (URISyntaxException use) {
|
||||
throw new IllegalArgumentException("Invalid EasStore URI", use);
|
||||
}
|
||||
|
||||
String scheme = easUri.getScheme();
|
||||
if (scheme.equals("eas")) {
|
||||
connectionSecurity = ConnectionSecurity.NONE;
|
||||
} else if (scheme.equals("eas+ssl")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_OPTIONAL;
|
||||
} else if (scheme.equals("eas+ssl+")) {
|
||||
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED;
|
||||
} else if (scheme.equals("eas+tls")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_OPTIONAL;
|
||||
} else if (scheme.equals("eas+tls+")) {
|
||||
connectionSecurity = ConnectionSecurity.STARTTLS_REQUIRED;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported protocol (" + scheme + ")");
|
||||
}
|
||||
|
||||
host = easUri.getHost();
|
||||
if (host.startsWith("http")) {
|
||||
String[] hostParts = host.split("://", 2);
|
||||
if (hostParts.length > 1) {
|
||||
host = hostParts[1];
|
||||
}
|
||||
}
|
||||
|
||||
port = easUri.getPort();
|
||||
|
||||
if (easUri.getUserInfo() != null) {
|
||||
try {
|
||||
String[] userInfoParts = easUri.getUserInfo().split(":");
|
||||
|
||||
username = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
|
||||
if (userInfoParts.length > 1) {
|
||||
password = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
throw new IllegalArgumentException("Couldn't urldecode username or password.", enc);
|
||||
}
|
||||
}
|
||||
|
||||
return new ServerSettings(STORE_TYPE, host, port, connectionSecurity, null, username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a EasStore URI with the supplied settings.
|
||||
*
|
||||
* @param server
|
||||
* The {@link ServerSettings} object that holds the server settings.
|
||||
*
|
||||
* @return A EasStore URI that holds the same information as the {@code server} parameter.
|
||||
*
|
||||
* @see Account#getStoreUri()
|
||||
* @see EasStore#decodeUri(String)
|
||||
*/
|
||||
public static String createUri(ServerSettings server) {
|
||||
String userEnc;
|
||||
String passwordEnc;
|
||||
try {
|
||||
userEnc = URLEncoder.encode(server.username, "UTF-8");
|
||||
passwordEnc = (server.password != null) ?
|
||||
URLEncoder.encode(server.password, "UTF-8") : "";
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("Could not encode username or password", e);
|
||||
}
|
||||
String userInfo = userEnc + ":" + passwordEnc;
|
||||
|
||||
String scheme;
|
||||
switch (server.connectionSecurity) {
|
||||
case SSL_TLS_OPTIONAL:
|
||||
scheme = "eas+ssl";
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
scheme = "eas+ssl+";
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
scheme = "eas+tls";
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
scheme = "eas+tls+";
|
||||
break;
|
||||
default:
|
||||
case NONE:
|
||||
scheme = "eas";
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URI(scheme, userInfo, server.host, server.port, null,
|
||||
null, null).toString();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException("Can't create EasStore URI", e);
|
||||
}
|
||||
}
|
||||
|
||||
// The following members are set during the first contact with the Exchange server, before
|
||||
// we provision or send any other requests. They are synchronized by mInitializationLock.
|
||||
private String mProtocolVersion = null;
|
||||
private Double mProtocolVersionDouble = null;
|
||||
private String mDeviceId = null;
|
||||
private Object mInitializationLock = new Object();
|
||||
private final String mDeviceType = "Android";
|
||||
|
||||
private String mHost;
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
private short mConnectionSecurity;
|
||||
private boolean mSecure;
|
||||
private HttpClient mHttpClient = null;
|
||||
private String mAuthString = null;
|
||||
private String mCmdString = null;
|
||||
|
||||
private HashMap<String, EasFolder> mFolderList = new HashMap<String, EasFolder>();
|
||||
|
||||
/**
|
||||
* eas://user:password@server:port CONNECTION_SECURITY_NONE
|
||||
* eas+tls://user:password@server:port CONNECTION_SECURITY_TLS_OPTIONAL
|
||||
* eas+tls+://user:password@server:port CONNECTION_SECURITY_TLS_REQUIRED
|
||||
* eas+ssl://user:password@server:port CONNECTION_SECURITY_SSL_OPTIONAL
|
||||
* eas+ssl+://user:password@server:port CONNECTION_SECURITY_SSL_REQUIRED
|
||||
*/
|
||||
public EasStore(Account account) throws MessagingException {
|
||||
super(account);
|
||||
|
||||
ServerSettings settings;
|
||||
try {
|
||||
mUri = new URI(mAccount.getStoreUri());
|
||||
} catch (URISyntaxException use) {
|
||||
throw new MessagingException("Invalid WebDavStore URI", use);
|
||||
settings = decodeUri(mAccount.getStoreUri());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new MessagingException("Error while decoding store URI", e);
|
||||
}
|
||||
|
||||
mHost = settings.host;
|
||||
mUsername = settings.username;
|
||||
mPassword = settings.password;
|
||||
|
||||
String scheme = mUri.getScheme();
|
||||
if (scheme.equals("eas")) {
|
||||
switch (settings.connectionSecurity) {
|
||||
case NONE:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_NONE;
|
||||
} else if (scheme.equals("eas+ssl")) {
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
||||
} else if (scheme.equals("eas+ssl+")) {
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
} else if (scheme.equals("eas+tls")) {
|
||||
break;
|
||||
case STARTTLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_OPTIONAL;
|
||||
} else if (scheme.equals("eas+tls+")) {
|
||||
break;
|
||||
case STARTTLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_TLS_REQUIRED;
|
||||
} else {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
|
||||
mHost = mUri.getHost();
|
||||
if (mHost.startsWith("http")) {
|
||||
String[] hostParts = mHost.split("://", 2);
|
||||
if (hostParts.length > 1) {
|
||||
mHost = hostParts[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (mUri.getUserInfo() != null) {
|
||||
try {
|
||||
String[] userInfoParts = mUri.getUserInfo().split(":");
|
||||
|
||||
mUsername = URLDecoder.decode(userInfoParts[0], "UTF-8");
|
||||
|
||||
if (userInfoParts.length > 1) {
|
||||
mPassword = URLDecoder.decode(userInfoParts[1], "UTF-8");
|
||||
}
|
||||
} catch (UnsupportedEncodingException enc) {
|
||||
// This shouldn't happen since the encoding is hardcoded to UTF-8
|
||||
Log.e(K9.LOG_TAG, "Couldn't urldecode username or password.", enc);
|
||||
}
|
||||
break;
|
||||
case SSL_TLS_OPTIONAL:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_OPTIONAL;
|
||||
break;
|
||||
case SSL_TLS_REQUIRED:
|
||||
mConnectionSecurity = CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
break;
|
||||
}
|
||||
|
||||
mSecure = mConnectionSecurity == CONNECTION_SECURITY_SSL_REQUIRED;
|
||||
@ -218,37 +321,18 @@ public class EasStore extends Store {
|
||||
|
||||
@Override
|
||||
public void checkSettings() throws MessagingException {
|
||||
boolean ssl = true;
|
||||
|
||||
boolean trustCertificates = true;
|
||||
|
||||
validateAccount(
|
||||
mHost,
|
||||
mUsername,
|
||||
mPassword,
|
||||
mUri.getPort(),
|
||||
ssl,
|
||||
trustCertificates,
|
||||
K9.app);
|
||||
validateAccount();
|
||||
}
|
||||
|
||||
public void validateAccount(String hostAddress, String userName, String password, int port,
|
||||
boolean ssl, boolean trustCertificates, Context context) throws MessagingException {
|
||||
public void validateAccount() throws MessagingException {
|
||||
try {
|
||||
Log.i(K9.LOG_TAG, "Testing EAS: " + hostAddress + ", " + userName + ", ssl = " + (ssl ? "1" : "0"));
|
||||
Log.i(K9.LOG_TAG, "Testing EAS: " + mHost + ", " + mUsername + ", ssl = " + (mSecure ? "1" : "0"));
|
||||
|
||||
// Account account = Preferences.getPreferences(context).newAccount();
|
||||
// account.setName("%TestAccount%");
|
||||
// account.setStoreUri(mUri.toString());
|
||||
EasStore svc = new EasStore(mAccount);
|
||||
svc.mHost = hostAddress;
|
||||
svc.mUsername = userName;
|
||||
svc.mPassword = password;
|
||||
// svc.mSsl = ssl;
|
||||
// svc.mTrustSsl = trustCertificates;
|
||||
// We mustn't use the "real" device id or we'll screw up current accounts
|
||||
// Any string will do, but we'll go for "validate"
|
||||
// We musn't use the "real" device id or we'll screw up current accounts.
|
||||
// Any string will do, but we'll go for "validate".
|
||||
svc.mDeviceId = "validate";
|
||||
|
||||
HttpResponse resp = svc.sendHttpClientOptions();
|
||||
reclaimConnection(resp);
|
||||
int code = resp.getStatusLine().getStatusCode();
|
||||
@ -319,7 +403,7 @@ public class EasStore extends Store {
|
||||
}
|
||||
|
||||
private String getPolicyType() {
|
||||
return (getProtocolVersion() >=
|
||||
return (getProtocolVersionDouble() >=
|
||||
Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) ? EAS_12_POLICY_TYPE : EAS_2_POLICY_TYPE;
|
||||
}
|
||||
|
||||
@ -424,7 +508,7 @@ public class EasStore extends Store {
|
||||
return (code == HTTP_NEED_PROVISIONING) || (code == HttpStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
private void setupProtocolVersion(EasStore service, Header versionHeader)
|
||||
private static void setupProtocolVersion(EasStore service, Header versionHeader)
|
||||
throws MessagingException {
|
||||
// The string is a comma separated list of EAS versions in ascending order
|
||||
// e.g. 1.0,2.0,2.5,12.0,12.1
|
||||
@ -445,13 +529,15 @@ public class EasStore extends Store {
|
||||
Log.w(K9.LOG_TAG, "No supported EAS versions: " + supportedVersions);
|
||||
throw new MessagingException("MessagingException.PROTOCOL_VERSION_UNSUPPORTED");
|
||||
} else {
|
||||
service.mProtocolVersion = ourVersion;
|
||||
service.mProtocolVersionDouble = Double.parseDouble(ourVersion);
|
||||
synchronized (service.mInitializationLock) {
|
||||
service.mProtocolVersion = ourVersion;
|
||||
service.mProtocolVersionDouble = Double.parseDouble(ourVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Double getProtocolVersion() {
|
||||
synchronized (mProtocolVersionDouble) {
|
||||
private Double getProtocolVersionDouble() {
|
||||
synchronized (mInitializationLock) {
|
||||
if (mProtocolVersionDouble == null) {
|
||||
try {
|
||||
init();
|
||||
@ -553,7 +639,9 @@ public class EasStore extends Store {
|
||||
*/
|
||||
private void setHeaders(HttpRequestBase method, boolean usePolicyKey) {
|
||||
method.setHeader("Authorization", mAuthString);
|
||||
method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
|
||||
synchronized (mInitializationLock) {
|
||||
method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
|
||||
}
|
||||
method.setHeader("Connection", "keep-alive");
|
||||
method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION);
|
||||
if (usePolicyKey) {
|
||||
@ -598,8 +686,11 @@ public class EasStore extends Store {
|
||||
String safeUserName = URLEncoder.encode(mUsername);
|
||||
String cs = mUsername + ':' + mPassword;
|
||||
mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
|
||||
mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId +
|
||||
"&DeviceType=" + mDeviceType;
|
||||
|
||||
synchronized (mInitializationLock) {
|
||||
mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId +
|
||||
"&DeviceType=" + mDeviceType;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -622,30 +713,41 @@ public class EasStore extends Store {
|
||||
}
|
||||
|
||||
private void init() throws IOException, MessagingException {
|
||||
// Determine our protocol version, if we haven't already and save it in the Account
|
||||
// Also re-check protocol version at least once a day (in case of upgrade)
|
||||
boolean lastSyncTimeDayDue = false;
|
||||
//lastSyncTimeDayDue = ((System.currentTimeMillis() - mMailbox.mSyncTime) > DAYS);
|
||||
if (mProtocolVersion == null || lastSyncTimeDayDue) {
|
||||
Log.d(K9.LOG_TAG, "Determine EAS protocol version");
|
||||
HttpResponse resp = sendHttpClientOptions();
|
||||
reclaimConnection(resp);
|
||||
int code = resp.getStatusLine().getStatusCode();
|
||||
Log.d(K9.LOG_TAG, "OPTIONS response: " + code);
|
||||
if (code == HttpStatus.SC_OK) {
|
||||
Header header = resp.getFirstHeader("MS-ASProtocolCommands");
|
||||
Log.d(K9.LOG_TAG, header.getValue());
|
||||
header = resp.getFirstHeader("ms-asprotocolversions");
|
||||
try {
|
||||
setupProtocolVersion(this, header);
|
||||
} catch (MessagingException e) {
|
||||
// Since we've already validated, this can't really happen
|
||||
// But if it does, we'll rethrow this...
|
||||
synchronized (mInitializationLock) {
|
||||
// Get a unique ID to identify the device and application.
|
||||
if (mDeviceId == null) {
|
||||
mDeviceId = K9.app.getDeviceId();
|
||||
if (mDeviceId.length() > MAX_DEVICE_ID_SIZE) {
|
||||
// This should not happen, since getDeviceId returns a UUID string with the dashes
|
||||
// removed, which is always 32 characters. Best to be safe.
|
||||
mDeviceId = mDeviceId.substring(0, MAX_DEVICE_ID_SIZE);
|
||||
}
|
||||
}
|
||||
// Determine our protocol version, if we haven't already and save it in the Account
|
||||
// Also re-check protocol version at least once a day (in case of upgrade)
|
||||
boolean lastSyncTimeDayDue = false;
|
||||
//lastSyncTimeDayDue = ((System.currentTimeMillis() - mMailbox.mSyncTime) > DAYS);
|
||||
if (mProtocolVersion == null || lastSyncTimeDayDue) {
|
||||
Log.d(K9.LOG_TAG, "Determine EAS protocol version");
|
||||
HttpResponse resp = sendHttpClientOptions();
|
||||
reclaimConnection(resp);
|
||||
int code = resp.getStatusLine().getStatusCode();
|
||||
Log.d(K9.LOG_TAG, "OPTIONS response: " + code);
|
||||
if (code == HttpStatus.SC_OK) {
|
||||
Header header = resp.getFirstHeader("MS-ASProtocolCommands");
|
||||
Log.d(K9.LOG_TAG, header.getValue());
|
||||
header = resp.getFirstHeader("ms-asprotocolversions");
|
||||
try {
|
||||
setupProtocolVersion(this, header);
|
||||
} catch (MessagingException e) {
|
||||
// Since we've already validated, this can't really happen
|
||||
// But if it does, we'll rethrow this...
|
||||
throw new IOException();
|
||||
}
|
||||
} else {
|
||||
Log.e(K9.LOG_TAG, "OPTIONS command failed; throwing IOException");
|
||||
throw new IOException();
|
||||
}
|
||||
} else {
|
||||
Log.e(K9.LOG_TAG, "OPTIONS command failed; throwing IOException");
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1086,7 +1188,7 @@ public class EasStore extends Store {
|
||||
// Enable MimeSupport
|
||||
s.data(Tags.SYNC_MIME_SUPPORT, "2");
|
||||
// Set the truncation amount for all classes.
|
||||
if (getProtocolVersion() >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
|
||||
if (getProtocolVersionDouble() >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
|
||||
s.start(Tags.BASE_BODY_PREFERENCE)
|
||||
// HTML for email; plain text for everything else.
|
||||
.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_MIME);
|
||||
|
Loading…
Reference in New Issue
Block a user