Convert authorities, intents, and account manager types
* Tested ok on wiped Nexus * Tested ok on S3 * Upgrade verified on Nexus Change-Id: If5d4ce594f8a309cdb59589d10b1d33f3b79326c
This commit is contained in:
parent
5ac8d38796
commit
e714bb9d15
|
@ -23,7 +23,7 @@ include $(CLEAR_VARS)
|
|||
chips_dir := ../../../frameworks/ex/chips/res
|
||||
unified_email_dir := ../UnifiedEmail
|
||||
photo_dir := ../../../frameworks/ex/photoviewer/res
|
||||
res_dir := $(chips_dir) res $(unified_email_dir)/res $(photo_dir)
|
||||
res_dir := $(chips_dir) res $(unified_email_dir)/res $(photo_dir) build/res
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.email"
|
||||
android:versionCode="500004"
|
||||
android:versionName="4.2-004" >
|
||||
android:versionCode="500005"
|
||||
android:versionName="4.2-005" >
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
@ -31,7 +31,9 @@
|
|||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
|
||||
<uses-permission android:name="android.permission.READ_PROFILE"/>
|
||||
<uses-permission android:name="android.permission.NFC"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
@ -413,7 +415,7 @@
|
|||
|
||||
<!--Required stanza to register the PopImapAuthenticatorService with AccountManager -->
|
||||
<service
|
||||
android:name=".service.PopImapAuthenticatorService"
|
||||
android:name=".service.Pop3AuthenticatorService"
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
>
|
||||
|
@ -423,20 +425,35 @@
|
|||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/pop_imap_authenticator"
|
||||
android:resource="@xml/authenticator_pop3"
|
||||
/>
|
||||
</service>
|
||||
<!--Required stanza to register the PopImapAuthenticatorService with AccountManager -->
|
||||
<service
|
||||
android:name=".service.ImapAuthenticatorService"
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
>
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator_imap"
|
||||
/>
|
||||
</service>
|
||||
|
||||
<!--Required stanza to register the PopImapSyncAdapterService with SyncManager -->
|
||||
<service
|
||||
android:name="com.android.email.service.PopImapSyncAdapterService"
|
||||
android:name="com.android.email.service.Pop3SyncAdapterService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/syncadapter_pop_imap" />
|
||||
android:resource="@xml/syncadapter_pop3" />
|
||||
</service>
|
||||
|
||||
<!-- Require provider permission to use our Policy and Account services -->
|
||||
|
@ -496,7 +513,7 @@
|
|||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/eas_authenticator"
|
||||
android:resource="@xml/authenticator_eas"
|
||||
/>
|
||||
</service>
|
||||
<!--Required stanza to register the EasTestAuthenticatorService with AccountManager -->
|
||||
|
@ -533,7 +550,7 @@
|
|||
/>
|
||||
</service>
|
||||
<service
|
||||
android:name=".service.Imap2AuthenticatorService"
|
||||
android:name=".service.ImapAuthenticatorService"
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
>
|
||||
|
@ -543,7 +560,7 @@
|
|||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/imap2_authenticator"
|
||||
android:resource="@xml/authenticator_imap"
|
||||
/>
|
||||
</service>
|
||||
|
||||
|
@ -555,7 +572,7 @@
|
|||
android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/syncadapter_imap2" />
|
||||
android:resource="@xml/syncadapter_imap" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
|
@ -583,5 +600,40 @@
|
|||
android:label="@string/app_name"
|
||||
/>
|
||||
|
||||
<!-- Legacy authenticators, etc. can be added below. OEMs may remove these -->
|
||||
|
||||
<service
|
||||
android:name=".service.LegacyEmailAuthenticatorService"
|
||||
android:exported="false"
|
||||
android:enabled="true"
|
||||
>
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator_legacy_email"
|
||||
/>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".service.LegacyEasAuthenticatorService"
|
||||
android:exported="false"
|
||||
android:enabled="true"
|
||||
>
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.accounts.AccountAuthenticator" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accounts.AccountAuthenticator"
|
||||
android:resource="@xml/authenticator_legacy_eas"
|
||||
/>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
<!-- Legacy permissions, etc. can go here -->
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="account_manager_type_exchange">com.android.exchange</string>
|
||||
<string name="account_manager_type_pop3">com.android.pop3</string>
|
||||
<string name="account_manager_type_imap">com.android.imap</string>
|
||||
<string name="intent_exchange">com.android.email.EXCHANGE_INTENT</string>
|
||||
<string name="authority_email_provider">com.android.email.provider</string>
|
||||
<string name="protocol_imap">imap</string>
|
||||
<string name="protocol_pop3">pop3</string>
|
||||
</resources>
|
|
@ -40,15 +40,6 @@ import java.util.UUID;
|
|||
|
||||
public final class Account extends EmailContent implements AccountColumns, Parcelable {
|
||||
public static final String TABLE_NAME = "Account";
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
|
||||
public static final Uri ADD_TO_FIELD_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");
|
||||
public static final Uri RESET_NEW_MESSAGE_COUNT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount");
|
||||
public static final Uri NOTIFIER_URI =
|
||||
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account");
|
||||
public static final Uri DEFAULT_ACCOUNT_ID_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/account/default");
|
||||
|
||||
// Define all pseudo account IDs here to avoid conflict with one another.
|
||||
/**
|
||||
|
@ -109,6 +100,19 @@ public final class Account extends EmailContent implements AccountColumns, Parce
|
|||
public static final int CHECK_INTERVAL_NEVER = -1;
|
||||
public static final int CHECK_INTERVAL_PUSH = -2;
|
||||
|
||||
public static Uri CONTENT_URI;
|
||||
public static Uri ADD_TO_FIELD_URI;
|
||||
public static Uri RESET_NEW_MESSAGE_COUNT_URI;
|
||||
public static Uri NOTIFIER_URI;
|
||||
public static Uri DEFAULT_ACCOUNT_ID_URI;
|
||||
|
||||
public static void initAccount() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
|
||||
ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");
|
||||
RESET_NEW_MESSAGE_COUNT_URI = Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount");
|
||||
NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account");
|
||||
DEFAULT_ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/account/default");
|
||||
}
|
||||
public String mDisplayName;
|
||||
public String mEmailAddress;
|
||||
public String mSyncKey;
|
||||
|
@ -495,7 +499,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
|
|||
public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) {
|
||||
// Make sure the URI is in the correct format.
|
||||
if (!"content".equals(uri.getScheme())
|
||||
|| !AUTHORITY.equals(uri.getAuthority())) {
|
||||
|| !EmailContent.AUTHORITY.equals(uri.getAuthority())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -699,7 +703,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
|
|||
.newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId))
|
||||
.withValues(cv).build());
|
||||
try {
|
||||
context.getContentResolver().applyBatch(AUTHORITY, ops);
|
||||
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops);
|
||||
return 1;
|
||||
} catch (RemoteException e) {
|
||||
// There is nothing to be done here; fail by returning 0
|
||||
|
@ -779,7 +783,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
|
|||
|
||||
try {
|
||||
ContentProviderResult[] results =
|
||||
context.getContentResolver().applyBatch(AUTHORITY, ops);
|
||||
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops);
|
||||
// If saving, set the mId's of the various saved objects
|
||||
if (recvIndex >= 0) {
|
||||
long newId = getId(results[recvIndex].uri);
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.os.Environment;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.emailcommon.utility.TextUtilities;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
@ -59,39 +60,12 @@ import java.util.ArrayList;
|
|||
*
|
||||
*/
|
||||
public abstract class EmailContent {
|
||||
|
||||
public static final String AUTHORITY = "com.android.email.provider";
|
||||
// The notifier authority is used to send notifications regarding changes to messages (insert,
|
||||
// delete, or update) and is intended as an optimization for use by clients of message list
|
||||
// cursors (initially, the email AppWidget).
|
||||
public static final String NOTIFIER_AUTHORITY = "com.android.email.notifier";
|
||||
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
|
||||
public static final String PARAMETER_LIMIT = "limit";
|
||||
|
||||
public static final Uri CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY);
|
||||
|
||||
public static final Uri PICK_TRASH_FOLDER_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/pickTrashFolder");
|
||||
public static final Uri PICK_SENT_FOLDER_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/pickSentFolder");
|
||||
|
||||
public static final Uri MAILBOX_NOTIFICATION_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/mailboxNotification");
|
||||
public static final String[] NOTIFICATION_PROJECTION =
|
||||
new String[] {MailboxColumns.ID, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT};
|
||||
public static final int NOTIFICATION_MAILBOX_ID_COLUMN = 0;
|
||||
public static final int NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN = 1;
|
||||
public static final int NOTIFICATION_MAILBOX_MESSAGE_COUNT_COLUMN = 2;
|
||||
|
||||
public static final Uri MAILBOX_MOST_RECENT_MESSAGE_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/mailboxMostRecentMessage");
|
||||
|
||||
public static final Uri ACCOUNT_CHECK_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/accountCheck");
|
||||
|
||||
public static final String PROVIDER_PERMISSION = "com.android.email.permission.ACCESS_PROVIDER";
|
||||
|
||||
// All classes share this
|
||||
public static final String RECORD_ID = "_id";
|
||||
|
||||
|
@ -139,6 +113,57 @@ public abstract class EmailContent {
|
|||
// Read the Content from a ContentCursor
|
||||
public abstract void restore (Cursor cursor);
|
||||
|
||||
|
||||
public static String PACKAGE_NAME;
|
||||
public static String EMAIL_PACKAGE_NAME;
|
||||
public static String AUTHORITY;
|
||||
// The notifier authority is used to send notifications regarding changes to messages (insert,
|
||||
// delete, or update) and is intended as an optimization for use by clients of message list
|
||||
// cursors (initially, the email AppWidget).
|
||||
public static String NOTIFIER_AUTHORITY;
|
||||
public static Uri CONTENT_URI;
|
||||
public static final String PARAMETER_LIMIT = "limit";
|
||||
public static Uri CONTENT_NOTIFIER_URI;
|
||||
public static Uri PICK_TRASH_FOLDER_URI;
|
||||
public static Uri PICK_SENT_FOLDER_URI;
|
||||
public static Uri MAILBOX_NOTIFICATION_URI;
|
||||
public static Uri MAILBOX_MOST_RECENT_MESSAGE_URI;
|
||||
public static Uri ACCOUNT_CHECK_URI;
|
||||
public static String PROVIDER_PERMISSION;
|
||||
|
||||
public static void init(Context context) {
|
||||
if (AUTHORITY == null) {
|
||||
PACKAGE_NAME = context.getPackageName();
|
||||
EMAIL_PACKAGE_NAME = PACKAGE_NAME;
|
||||
// If our package is com...exchange, the provider is com...email.provider
|
||||
if (PACKAGE_NAME.endsWith("exchange")) {
|
||||
int lastDot = EMAIL_PACKAGE_NAME.lastIndexOf('.');
|
||||
EMAIL_PACKAGE_NAME = PACKAGE_NAME.substring(0, lastDot + 1) + "email";
|
||||
}
|
||||
AUTHORITY = EMAIL_PACKAGE_NAME + ".provider";
|
||||
Log.d("EmailContent", "init for " + AUTHORITY);
|
||||
NOTIFIER_AUTHORITY = EMAIL_PACKAGE_NAME + ".notifier";
|
||||
CONTENT_URI = Uri.parse("content://" + AUTHORITY);
|
||||
CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY);
|
||||
PICK_TRASH_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickTrashFolder");
|
||||
PICK_SENT_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickSendFolder");
|
||||
MAILBOX_NOTIFICATION_URI = Uri.parse("content://" + AUTHORITY + "/mailboxNotification");
|
||||
MAILBOX_MOST_RECENT_MESSAGE_URI = Uri.parse("content://" + AUTHORITY +
|
||||
"/mailboxMostRecentMessage");
|
||||
ACCOUNT_CHECK_URI = Uri.parse("content://" + AUTHORITY + "/accountCheck");
|
||||
PROVIDER_PERMISSION = EMAIL_PACKAGE_NAME + ".permission.ACCESS_PROVIDER";
|
||||
// Initialize subclasses
|
||||
Account.initAccount();
|
||||
Mailbox.initMailbox();
|
||||
QuickResponse.initQuickResponse();
|
||||
HostAuth.initHostAuth();
|
||||
Policy.initPolicy();
|
||||
Message.initMessage();
|
||||
Body.initBody();
|
||||
Attachment.initAttachment();
|
||||
}
|
||||
}
|
||||
|
||||
// The Uri is lazily initialized
|
||||
public Uri getUri() {
|
||||
if (mUri == null) {
|
||||
|
@ -287,8 +312,11 @@ public abstract class EmailContent {
|
|||
public static final class Body extends EmailContent implements BodyColumns {
|
||||
public static final String TABLE_NAME = "Body";
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
|
||||
public static Uri CONTENT_URI;
|
||||
|
||||
public static void initBody() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
|
||||
}
|
||||
|
||||
public static final int CONTENT_ID_COLUMN = 0;
|
||||
public static final int CONTENT_MESSAGE_KEY_COLUMN = 1;
|
||||
|
@ -330,7 +358,7 @@ public abstract class EmailContent {
|
|||
public static final String[] COMMON_PROJECTION_SOURCE = new String[] {
|
||||
RECORD_ID, BodyColumns.SOURCE_MESSAGE_KEY
|
||||
};
|
||||
public static final int COMMON_PROJECTION_COLUMN_TEXT = 1;
|
||||
public static final int COMMON_PROJECTION_COLUMN_TEXT = 1;
|
||||
|
||||
private static final String[] PROJECTION_SOURCE_KEY =
|
||||
new String[] { BodyColumns.SOURCE_MESSAGE_KEY };
|
||||
|
@ -558,18 +586,28 @@ public abstract class EmailContent {
|
|||
public static final String DELETED_TABLE_NAME = "Message_Deletes";
|
||||
|
||||
// To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
|
||||
public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
|
||||
public static final Uri SYNCED_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
|
||||
public static final Uri SELECTED_MESSAGE_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/messageBySelection");
|
||||
public static final Uri DELETED_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
|
||||
public static final Uri UPDATED_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
|
||||
public static final Uri NOTIFIER_URI =
|
||||
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
|
||||
public static Uri CONTENT_URI;
|
||||
public static Uri CONTENT_URI_LIMIT_1;
|
||||
public static Uri SYNCED_CONTENT_URI;
|
||||
public static Uri SELECTED_MESSAGE_CONTENT_URI ;
|
||||
public static Uri DELETED_CONTENT_URI;
|
||||
public static Uri UPDATED_CONTENT_URI;
|
||||
public static Uri NOTIFIER_URI;
|
||||
|
||||
public static void initMessage() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
|
||||
CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
|
||||
SYNCED_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
|
||||
SELECTED_MESSAGE_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/messageBySelection");
|
||||
DELETED_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
|
||||
UPDATED_CONTENT_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
|
||||
NOTIFIER_URI =
|
||||
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
|
||||
}
|
||||
|
||||
public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc";
|
||||
|
||||
|
@ -1143,11 +1181,16 @@ public abstract class EmailContent {
|
|||
public static final class Attachment extends EmailContent
|
||||
implements AttachmentColumns, Parcelable {
|
||||
public static final String TABLE_NAME = "Attachment";
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
|
||||
|
||||
public static Uri CONTENT_URI;
|
||||
// This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id)
|
||||
public static final Uri MESSAGE_ID_URI = Uri.parse(
|
||||
EmailContent.CONTENT_URI + "/attachment/message");
|
||||
public static Uri MESSAGE_ID_URI;
|
||||
|
||||
public static void initAttachment() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
|
||||
MESSAGE_ID_URI = Uri.parse(
|
||||
EmailContent.CONTENT_URI + "/attachment/message");
|
||||
}
|
||||
|
||||
public String mFileName;
|
||||
public String mMimeType;
|
||||
|
|
|
@ -34,7 +34,11 @@ import java.net.URISyntaxException;
|
|||
|
||||
public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable {
|
||||
public static final String TABLE_NAME = "HostAuth";
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
|
||||
public static Uri CONTENT_URI;
|
||||
|
||||
public static void initHostAuth() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
|
||||
}
|
||||
|
||||
// These legacy constants should be used in code created prior to Email2
|
||||
public static final String LEGACY_SCHEME_IMAP = "imap";
|
||||
|
@ -42,11 +46,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
|
|||
public static final String LEGACY_SCHEME_EAS = "eas";
|
||||
public static final String LEGACY_SCHEME_SMTP = "smtp";
|
||||
|
||||
// These constants should, over time, be replaced by information in services.xml
|
||||
public static final String SCHEME_IMAP = "imap";
|
||||
public static final String SCHEME_POP3 = "pop3";
|
||||
public static final String SCHEME_EAS = "eas";
|
||||
|
||||
public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts";
|
||||
|
||||
public static final int PORT_UNKNOWN = -1;
|
||||
|
@ -284,10 +283,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
|
|||
mClientCertAlias = clientCertAlias;
|
||||
}
|
||||
|
||||
/** Returns {@code true} if this is an EAS connection; otherwise, {@code false}. */
|
||||
public boolean isEasConnection() {
|
||||
return SCHEME_EAS.equals(mProtocol);
|
||||
}
|
||||
|
||||
/** Convenience method to determine if SSL is used. */
|
||||
public boolean shouldUseSsl() {
|
||||
|
|
|
@ -33,11 +33,17 @@ import com.android.emailcommon.utility.Utility;
|
|||
|
||||
public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns, Parcelable {
|
||||
public static final String TABLE_NAME = "Mailbox";
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
|
||||
public static final Uri ADD_TO_FIELD_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
|
||||
public static final Uri FROM_ACCOUNT_AND_TYPE_URI =
|
||||
Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdFromAccountAndType");
|
||||
|
||||
public static Uri CONTENT_URI;
|
||||
public static Uri ADD_TO_FIELD_URI;
|
||||
public static Uri FROM_ACCOUNT_AND_TYPE_URI;
|
||||
|
||||
public static void initMailbox() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
|
||||
ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
|
||||
FROM_ACCOUNT_AND_TYPE_URI = Uri.parse(EmailContent.CONTENT_URI +
|
||||
"/mailboxIdFromAccountAndType");
|
||||
}
|
||||
|
||||
public String mDisplayName;
|
||||
public String mServerId;
|
||||
|
|
|
@ -40,8 +40,11 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
|
|||
public static final String TAG = "Email/Policy";
|
||||
|
||||
public static final String TABLE_NAME = "Policy";
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy");
|
||||
public static Uri CONTENT_URI;
|
||||
|
||||
public static void initPolicy() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy");
|
||||
}
|
||||
|
||||
/* Convert days to mSec (used for password expiration) */
|
||||
private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000;
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
|
||||
package com.android.emailcommon.provider;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
@ -29,6 +25,9 @@ import android.net.Uri;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* A user-modifiable message that may be quickly inserted into the body while user is composing
|
||||
* a message. Tied to a specific account.
|
||||
|
@ -36,11 +35,13 @@ import android.os.Parcelable;
|
|||
public final class QuickResponse extends EmailContent
|
||||
implements QuickResponseColumns, Parcelable {
|
||||
public static final String TABLE_NAME = "QuickResponse";
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI
|
||||
+ "/quickresponse");
|
||||
public static final Uri ACCOUNT_ID_URI = Uri.parse(
|
||||
EmailContent.CONTENT_URI + "/quickresponse/account");
|
||||
public static Uri CONTENT_URI;
|
||||
public static Uri ACCOUNT_ID_URI;
|
||||
|
||||
public static void initQuickResponse() {
|
||||
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse");
|
||||
ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse/account");
|
||||
}
|
||||
|
||||
private String mText;
|
||||
private long mAccountKey;
|
||||
|
|
|
@ -24,14 +24,13 @@ import android.os.RemoteException;
|
|||
|
||||
public class AccountServiceProxy extends ServiceProxy implements IAccountService {
|
||||
|
||||
public static final String ACCOUNT_INTENT = "com.android.email.ACCOUNT_INTENT";
|
||||
public static final int DEFAULT_ACCOUNT_COLOR = 0xFF0000FF;
|
||||
|
||||
private IAccountService mService = null;
|
||||
private Object mReturn;
|
||||
|
||||
public AccountServiceProxy(Context _context) {
|
||||
super(_context, new Intent(ACCOUNT_INTENT));
|
||||
super(_context, getIntentForEmailPackage(_context, "ACCOUNT_INTENT"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -475,6 +475,23 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService {
|
|||
return (Integer)mReturn;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Request that the account be updated for this service; this call is synchronous
|
||||
*
|
||||
* @param the email address of the account to be updated
|
||||
*/
|
||||
@Override
|
||||
public void serviceUpdated(final String emailAddress) throws RemoteException {
|
||||
setTask(new ProxyTask() {
|
||||
@Override
|
||||
public void run() throws RemoteException{
|
||||
if (mCallback != null) mService.setCallback(mCallback);
|
||||
mService.serviceUpdated(emailAddress);
|
||||
}
|
||||
}, "settingsUpdate");
|
||||
waitForCompletion();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
|
|
|
@ -63,4 +63,6 @@ interface IEmailService {
|
|||
|
||||
// API level 3
|
||||
int getCapabilities(in Account acct);
|
||||
|
||||
void serviceUpdated(String emailAddress);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package com.android.emailcommon.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
@ -29,14 +28,11 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
|
|||
private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE
|
||||
private static final String TAG = "PolicyServiceProxy";
|
||||
|
||||
// The intent used by sync adapter services to connect to the PolicyService
|
||||
public static final String POLICY_INTENT = "com.android.email.POLICY_INTENT";
|
||||
|
||||
private IPolicyService mService = null;
|
||||
private Object mReturn = null;
|
||||
|
||||
public PolicyServiceProxy(Context _context) {
|
||||
super(_context, new Intent(POLICY_INTENT));
|
||||
super(_context, getIntentForEmailPackage(_context, "POLICY_INTENT"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -56,6 +56,16 @@ public abstract class ServiceProxy {
|
|||
private long mStartTime;
|
||||
private boolean mDead = false;
|
||||
|
||||
public static Intent getIntentForEmailPackage(Context context, String actionName) {
|
||||
String packageName = context.getPackageName();
|
||||
int lastDot = packageName.lastIndexOf('.');
|
||||
String intentString =
|
||||
packageName.substring(0, lastDot + 1) + "email." + actionName;
|
||||
// STOPSHIP Remove logging
|
||||
Log.d("ServiceProxy", actionName + " -> " + intentString);
|
||||
return new Intent(intentString);
|
||||
}
|
||||
|
||||
public abstract void onConnected(IBinder binder);
|
||||
|
||||
public ServiceProxy(Context _context, Intent _intent) {
|
||||
|
|
|
@ -30,6 +30,7 @@ import android.util.Log;
|
|||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.Attachment;
|
||||
import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.Body;
|
||||
|
@ -47,8 +48,6 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
|
||||
public class AttachmentUtilities {
|
||||
public static final String AUTHORITY = "com.android.email.attachmentprovider";
|
||||
public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY);
|
||||
|
||||
public static final String FORMAT_RAW = "RAW";
|
||||
public static final String FORMAT_THUMBNAIL = "THUMBNAIL";
|
||||
|
@ -137,23 +136,17 @@ public class AttachmentUtilities {
|
|||
*/
|
||||
public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024);
|
||||
|
||||
private static Uri sUri;
|
||||
public static Uri getAttachmentUri(long accountId, long id) {
|
||||
return CONTENT_URI.buildUpon()
|
||||
.appendPath(Long.toString(accountId))
|
||||
.appendPath(Long.toString(id))
|
||||
.appendPath(FORMAT_RAW)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Uri getAttachmentThumbnailUri(long accountId, long id,
|
||||
int width, int height) {
|
||||
return CONTENT_URI.buildUpon()
|
||||
.appendPath(Long.toString(accountId))
|
||||
.appendPath(Long.toString(id))
|
||||
.appendPath(FORMAT_THUMBNAIL)
|
||||
.appendPath(Integer.toString(width))
|
||||
.appendPath(Integer.toString(height))
|
||||
.build();
|
||||
if (sUri == null) {
|
||||
sUri = Uri.parse("content://" + EmailContent.EMAIL_PACKAGE_NAME +
|
||||
".attachmentprovider");
|
||||
}
|
||||
return sUri.buildUpon()
|
||||
.appendPath(Long.toString(accountId))
|
||||
.appendPath(Long.toString(id))
|
||||
.appendPath(FORMAT_RAW)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,16 +17,16 @@
|
|||
|
||||
package com.android.emailsync;
|
||||
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
|
@ -48,7 +48,6 @@ public abstract class AbstractSyncService implements Runnable {
|
|||
public static final int CONNECT_TIMEOUT = 30*SECONDS;
|
||||
public static final int NETWORK_WAIT = 15*SECONDS;
|
||||
|
||||
public static final String EAS_PROTOCOL = "eas";
|
||||
public static final int EXIT_DONE = 0;
|
||||
public static final int EXIT_IO_ERROR = 1;
|
||||
public static final int EXIT_LOGIN_FAILURE = 2;
|
||||
|
@ -289,7 +288,9 @@ public abstract class AbstractSyncService implements Runnable {
|
|||
*/
|
||||
|
||||
public void addRequest(Request req) {
|
||||
mRequestQueue.offer(req);
|
||||
if (!mRequestQueue.contains(req)) {
|
||||
mRequestQueue.offer(req);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRequest(Request req) {
|
||||
|
|
|
@ -1357,6 +1357,7 @@ public abstract class SyncManager extends Service implements Runnable {
|
|||
@Override
|
||||
public void onCreate() {
|
||||
TAG = getClass().getSimpleName();
|
||||
EmailContent.init(this);
|
||||
Utility.runAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
|
@ -64,5 +64,6 @@
|
|||
<enum name="mins60" value="60"/>
|
||||
</attr>
|
||||
<attr name="inferPrefix" format="string"/>
|
||||
<attr name="requiresAccountUpdate" format="boolean"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
@ -1310,8 +1310,9 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
|
|||
<!-- Displayed in the middle of the screen when the inbox is empty [CHAR LIMIT 100]-->
|
||||
<string name="no_conversations">No messages.</string>
|
||||
|
||||
<!-- Temporary; used by AccountManager -->
|
||||
<string name="imap2_name">Push IMAP</string>
|
||||
<!-- Used by AccountManager -->
|
||||
<string name="imap2_name">IMAP</string>
|
||||
<string name="pop3_name">POP3</string>
|
||||
|
||||
<string name="folder_picker_title">Picky, picky, picky!</string>
|
||||
<!-- Displayed when the user must pick his server's trash folder from a list [CHAR LIMIT 30]-->
|
||||
|
@ -1319,6 +1320,6 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
|
|||
<!-- Displayed when the user must pick his server's sent items folder from a list [CHAR LIMIT 30]-->
|
||||
<string name="sent_folder_selection_title">Select server sent items folder</string>
|
||||
<string name="create_new_folder">Create folder</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
<!-- The only difference from authenticator.xml is android:label. -->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="com.android.exchange"
|
||||
android:icon="@drawable/ic_exchange_selected"
|
||||
android:smallIcon="@drawable/ic_exchange_minitab_selected"
|
||||
android:accountType="@string/account_manager_type_exchange"
|
||||
android:icon="@mipmap/ic_launcher_mail"
|
||||
android:smallIcon="@drawable/stat_notify_email_generic"
|
||||
android:label="@string/exchange_name_alternate"
|
||||
android:accountPreferences="@xml/account_preferences"
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/**
|
||||
* Copyright (c) 2009, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<!-- The attributes in this XML file provide configuration information -->
|
||||
<!-- for the Account Manager. -->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_manager_type_exchange"
|
||||
android:icon="@mipmap/ic_launcher_mail"
|
||||
android:smallIcon="@drawable/stat_notify_email_generic"
|
||||
android:label="@string/exchange_name"
|
||||
android:accountPreferences="@xml/account_preferences"
|
||||
/>
|
|
@ -21,7 +21,7 @@
|
|||
<!-- for the Account Manager. -->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="com.android.imap2"
|
||||
android:accountType="@string/account_manager_type_imap"
|
||||
android:icon="@mipmap/ic_launcher_mail"
|
||||
android:smallIcon="@drawable/stat_notify_email_generic"
|
||||
android:label="@string/imap2_name"
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="com.android.exchange"
|
||||
android:icon="@drawable/ic_exchange_selected"
|
||||
android:smallIcon="@drawable/ic_exchange_minitab_selected"
|
||||
android:icon="@mipmap/ic_launcher_mail"
|
||||
android:smallIcon="@drawable/stat_notify_email_generic"
|
||||
android:label="@string/exchange_name"
|
||||
android:accountPreferences="@xml/account_preferences"
|
||||
/>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/**
|
||||
* Copyright (c) 2009, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
-->
|
||||
|
||||
<!-- The attributes in this XML file provide configuration information -->
|
||||
<!-- for the Account Manager. -->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="com.android.email"
|
||||
android:icon="@mipmap/ic_launcher_mail"
|
||||
android:smallIcon="@drawable/stat_notify_email_generic"
|
||||
android:label="@string/exchange_name"
|
||||
android:accountPreferences="@xml/account_preferences"
|
||||
/>
|
|
@ -21,9 +21,9 @@
|
|||
<!-- for the Account Manager. -->
|
||||
|
||||
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="com.android.email"
|
||||
android:accountType="@string/account_manager_type_pop3"
|
||||
android:icon="@mipmap/ic_launcher_mail"
|
||||
android:smallIcon="@drawable/stat_notify_email_generic"
|
||||
android:label="@string/app_name"
|
||||
android:label="@string/pop3_name"
|
||||
android:accountPreferences="@xml/account_preferences"
|
||||
/>
|
|
@ -31,5 +31,4 @@
|
|||
|
||||
<senders>
|
||||
<sender scheme="smtp" class="com.android.email.mail.transport.SmtpSender" />
|
||||
<sender scheme="eas" class="com.android.email.mail.transport.ExchangeSender" />
|
||||
</senders>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!-- for the SyncAdapter. -->
|
||||
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentAuthority="com.android.email.provider"
|
||||
android:accountType="com.android.imap2"
|
||||
android:contentAuthority="@string/authority_email_provider"
|
||||
android:accountType="@string/account_manager_type_imap"
|
||||
android:supportsUploading="true"
|
||||
/>
|
|
@ -21,8 +21,8 @@
|
|||
<!-- for the SyncAdapter. -->
|
||||
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentAuthority="com.android.email.provider"
|
||||
android:accountType="com.android.email"
|
||||
android:contentAuthority="@string/authority_email_provider"
|
||||
android:accountType="@string/account_manager_type_pop3"
|
||||
android:supportsUploading="true"
|
||||
android:allowParallelSyncs="true"
|
||||
/>
|
|
@ -124,6 +124,7 @@ public class NotificationController {
|
|||
@VisibleForTesting
|
||||
NotificationController(Context context, Clock clock) {
|
||||
mContext = context.getApplicationContext();
|
||||
EmailContent.init(context);
|
||||
mNotificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
|
|
|
@ -33,6 +33,8 @@ import android.util.Log;
|
|||
import com.android.email.R;
|
||||
import com.android.email.mail.Sender;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.service.EmailServiceUtils;
|
||||
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
|
@ -473,6 +475,11 @@ public class AccountCheckSettingsFragment extends Fragment {
|
|||
if (bundle != null) {
|
||||
resultCode = bundle.getInt(
|
||||
EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE);
|
||||
// Only show "policies required" if this is a new account setup
|
||||
if (resultCode == MessagingException.SECURITY_POLICIES_REQUIRED &&
|
||||
mAccount.isSaved()) {
|
||||
resultCode = MessagingException.NO_ERROR;
|
||||
}
|
||||
}
|
||||
if (resultCode == MessagingException.SECURITY_POLICIES_REQUIRED) {
|
||||
SetupData.setPolicy((Policy)bundle.getParcelable(
|
||||
|
@ -491,8 +498,11 @@ public class AccountCheckSettingsFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
String protocol = mAccount.mHostAuthRecv.mProtocol;
|
||||
EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext, protocol);
|
||||
|
||||
// Check Outgoing Settings
|
||||
if ((mMode & SetupData.CHECK_OUTGOING) != 0) {
|
||||
if (info.usesSmtp && (mMode & SetupData.CHECK_OUTGOING) != 0) {
|
||||
if (isCancelled()) return null;
|
||||
Log.d(Logging.LOG_TAG, "Begin check of outgoing email settings");
|
||||
publishProgress(STATE_CHECK_OUTGOING);
|
||||
|
|
|
@ -41,7 +41,6 @@ import com.android.email.SecurityPolicy;
|
|||
import com.android.email.service.EmailServiceUtils;
|
||||
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
import com.android.emailcommon.AccountManagerTypes;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
|
@ -727,7 +726,7 @@ public class AccountSettingsFragment extends EmailPreferenceFragment
|
|||
EmailServiceUtils.getServiceInfo(mContext, mAccount.getProtocol(mContext));
|
||||
if (info.syncContacts || info.syncCalendar) {
|
||||
android.accounts.Account acct = new android.accounts.Account(mAccount.mEmailAddress,
|
||||
AccountManagerTypes.TYPE_EXCHANGE);
|
||||
info.accountType);
|
||||
ContentResolver.setSyncAutomatically(acct, ContactsContract.AUTHORITY,
|
||||
mSyncContacts.isChecked());
|
||||
ContentResolver.setSyncAutomatically(acct, CalendarContract.AUTHORITY,
|
||||
|
|
|
@ -457,12 +457,6 @@ public class AccountSetupBasics extends AccountSetupActivity
|
|||
HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
|
||||
HostAuth.setHostAuthFromString(recvAuth, mProvider.incomingUri);
|
||||
|
||||
// STOPSHIP; Use for Imap2 testing
|
||||
if (HostAuth.LEGACY_SCHEME_IMAP.equals(recvAuth.mProtocol) &&
|
||||
EmailServiceUtils.isServiceAvailable(this, "imap2")) {
|
||||
recvAuth.mProtocol = "imap2";
|
||||
}
|
||||
|
||||
recvAuth.setLogin(mProvider.incomingUsername, password);
|
||||
EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this, recvAuth.mProtocol);
|
||||
recvAuth.mPort =
|
||||
|
|
|
@ -42,10 +42,6 @@ public class SetupData implements Parcelable {
|
|||
public static final int FLOW_MODE_RETURN_NO_ACCOUNTS_RESULT = 7;
|
||||
public static final int FLOW_MODE_NO_ACCOUNTS = 8;
|
||||
|
||||
// For debug logging
|
||||
private static final String[] FLOW_MODES = {"normal", "eas", "pop/imap", "edit", "force",
|
||||
"rtc", "rtl"};
|
||||
|
||||
// Mode bits for AccountSetupCheckSettings, indicating the type of check requested
|
||||
public static final int CHECK_INCOMING = 1;
|
||||
public static final int CHECK_OUTGOING = 2;
|
||||
|
@ -271,7 +267,6 @@ public class SetupData implements Parcelable {
|
|||
public static String debugString() {
|
||||
StringBuilder sb = new StringBuilder("SetupData");
|
||||
SetupData data = getInstance();
|
||||
sb.append(":flow=" + FLOW_MODES[data.mFlowMode]);
|
||||
sb.append(":acct=" + (data.mAccount == null ? "none" : data.mAccount.mId));
|
||||
if (data.mUsername != null) {
|
||||
sb.append(":user=" + data.mUsername);
|
||||
|
|
|
@ -22,8 +22,8 @@ import com.android.email.imap2.Imap2SyncService.Connection;
|
|||
import com.android.emailcommon.provider.EmailContent.Attachment;
|
||||
import com.android.emailcommon.provider.EmailContent.Message;
|
||||
import com.android.emailcommon.service.EmailServiceStatus;
|
||||
import com.android.emailsync.PartRequest;
|
||||
import com.android.emailcommon.utility.AttachmentUtilities;
|
||||
import com.android.emailsync.PartRequest;
|
||||
import com.android.mail.providers.UIProvider;
|
||||
|
||||
import org.apache.james.mime4j.decoder.Base64InputStream;
|
||||
|
@ -177,7 +177,7 @@ public class AttachmentLoader {
|
|||
OutputStream os = null;
|
||||
File tmpFile = null;
|
||||
try {
|
||||
tmpFile = File.createTempFile("imap2_", "tmp", mContext.getCacheDir());
|
||||
tmpFile = File.createTempFile("imap_", "tmp", mContext.getCacheDir());
|
||||
os = new FileOutputStream(tmpFile);
|
||||
String tag = mService.writeCommand(conn.writer, "uid fetch " + mMessage.mServerId +
|
||||
" body[" + mAttachment.mLocation + ']');
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
|
||||
package com.android.email.imap2;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.app.Service;
|
||||
|
@ -35,8 +30,13 @@ import android.os.Bundle;
|
|||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
|
||||
public class EmailSyncAdapterService extends Service {
|
||||
private static final String TAG = "Imap2 EmailSyncAdapterService";
|
||||
private static final String TAG = "Imap EmailSyncAdapterService";
|
||||
private static SyncAdapterImpl sSyncAdapter = null;
|
||||
private static final Object sSyncAdapterLock = new Object();
|
||||
|
||||
|
|
|
@ -28,9 +28,11 @@ import android.os.IBinder;
|
|||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.email.R;
|
||||
import com.android.emailcommon.Api;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.Attachment;
|
||||
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
|
@ -42,10 +44,10 @@ import com.android.emailcommon.service.IEmailService;
|
|||
import com.android.emailcommon.service.IEmailServiceCallback;
|
||||
import com.android.emailcommon.service.IEmailServiceCallback.Stub;
|
||||
import com.android.emailcommon.service.SearchParams;
|
||||
import com.android.emailcommon.service.SyncWindow;
|
||||
import com.android.emailsync.AbstractSyncService;
|
||||
import com.android.emailsync.PartRequest;
|
||||
import com.android.emailsync.SyncManager;
|
||||
import com.android.email.R;
|
||||
import com.android.mail.providers.UIProvider;
|
||||
import com.android.mail.providers.UIProvider.AccountCapabilities;
|
||||
import com.android.mail.providers.UIProvider.LastSyncResult;
|
||||
|
@ -64,8 +66,7 @@ public class Imap2SyncManager extends SyncManager {
|
|||
|
||||
private Intent mIntent;
|
||||
|
||||
private static final String IMAP2_ACCOUNT_TYPE = "com.android.imap2";
|
||||
private static final String PROTOCOL = "imap2";
|
||||
private static String PROTOCOL;
|
||||
|
||||
/**
|
||||
* Create our EmailService implementation here.
|
||||
|
@ -221,6 +222,30 @@ public class Imap2SyncManager extends SyncManager {
|
|||
AccountCapabilities.FOLDER_SERVER_SEARCH |
|
||||
AccountCapabilities.UNDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serviceUpdated(String emailAddress) throws RemoteException {
|
||||
SyncManager ssm = INSTANCE;
|
||||
if (ssm == null) return;
|
||||
log("serviceUpdated called for " + emailAddress);
|
||||
Cursor c = ssm.getContentResolver().query(Account.CONTENT_URI, Account.ID_PROJECTION,
|
||||
AccountColumns.EMAIL_ADDRESS + "=?", new String[] { emailAddress }, null);
|
||||
if (c == null) return;
|
||||
try {
|
||||
if (c.moveToNext()) {
|
||||
long accountId = c.getLong(0);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(AccountColumns.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
|
||||
values.put(AccountColumns.SYNC_LOOKBACK, SyncWindow.SYNC_WINDOW_AUTO);
|
||||
// Say we can push (at least, we'll try)
|
||||
mResolver.update(ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
|
||||
values, null, null);
|
||||
log("Sync interval and lookback set for " + emailAddress);
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static public IEmailServiceCallback callback() {
|
||||
|
@ -305,11 +330,14 @@ public class Imap2SyncManager extends SyncManager {
|
|||
// We must throw here; callers might use the information we provide for reconciliation, etc.
|
||||
if (c == null) throw new ProviderUnavailableException();
|
||||
try {
|
||||
if (PROTOCOL == null) {
|
||||
PROTOCOL = getString(R.string.protocol_imap);
|
||||
}
|
||||
while (c.moveToNext()) {
|
||||
long hostAuthId = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN);
|
||||
if (hostAuthId > 0) {
|
||||
HostAuth ha = HostAuth.restoreHostAuthWithId(context, hostAuthId);
|
||||
if (ha != null && ha.mProtocol.equals("imap2")) {
|
||||
if (ha != null && ha.mProtocol.equals(PROTOCOL)) {
|
||||
Account account = new Account();
|
||||
account.restore(c);
|
||||
account.mHostAuthRecv = ha;
|
||||
|
@ -325,7 +353,7 @@ public class Imap2SyncManager extends SyncManager {
|
|||
|
||||
@Override
|
||||
public String getAccountManagerType() {
|
||||
return IMAP2_ACCOUNT_TYPE;
|
||||
return getString(R.string.account_manager_type_imap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -344,7 +372,8 @@ public class Imap2SyncManager extends SyncManager {
|
|||
@Override
|
||||
protected void runAccountReconcilerSync(Context context) {
|
||||
alwaysLog("Reconciling accounts...");
|
||||
new AccountServiceProxy(context).reconcileAccounts(PROTOCOL, getAccountManagerType());
|
||||
new AccountServiceProxy(context).reconcileAccounts(
|
||||
getString(R.string.protocol_imap), getAccountManagerType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -149,7 +149,7 @@ public class Imap2SyncService extends AbstractSyncService {
|
|||
|
||||
private final String[] MAILBOX_SERVER_ID_ARGS = new String[2];
|
||||
public Imap2SyncService() {
|
||||
this("Imap2 Validation");
|
||||
this("Imap Validation");
|
||||
}
|
||||
|
||||
private final ArrayList<Integer> SERVER_DELETES = new ArrayList<Integer>();
|
||||
|
@ -186,7 +186,7 @@ public class Imap2SyncService extends AbstractSyncService {
|
|||
}
|
||||
|
||||
public Imap2SyncService(Context _context, Account _account) {
|
||||
this("Imap2 Account");
|
||||
this("Imap Account");
|
||||
mContext = _context;
|
||||
mResolver = _context.getContentResolver();
|
||||
mAccount = _account;
|
||||
|
@ -1512,8 +1512,9 @@ public class Imap2SyncService extends AbstractSyncService {
|
|||
if (andClause != null) {
|
||||
ac = ac + andClause;
|
||||
}
|
||||
// Add "+0" to the sort order to coerce the text field to an integer
|
||||
Cursor c = mResolver.query(Message.CONTENT_URI, UID_PROJECTION,
|
||||
ac, new String[] {Long.toString(mMailboxId)}, SyncColumns.SERVER_ID);
|
||||
ac, new String[] {Long.toString(mMailboxId)}, SyncColumns.SERVER_ID + "+0");
|
||||
if (c != null) {
|
||||
try {
|
||||
int[] uids = new int[c.getCount()];
|
||||
|
@ -1521,6 +1522,7 @@ public class Imap2SyncService extends AbstractSyncService {
|
|||
do {
|
||||
uids[offs++] = c.getInt(0);
|
||||
} while (c.moveToNext());
|
||||
System.err.println(uids);
|
||||
return uids;
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* Copyright (C) 2008 The Android Open Source P-roject
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -20,7 +20,7 @@ import android.content.Context;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.mail.store.ImapStore;
|
||||
import com.android.email.R;
|
||||
import com.android.email.mail.store.Pop3Store;
|
||||
import com.android.email.mail.store.ServiceStore;
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
|
@ -58,11 +58,6 @@ public abstract class Store {
|
|||
static final HashMap<String, Class<? extends Store>> sStoreClasses =
|
||||
new HashMap<String, Class<? extends Store>>();
|
||||
|
||||
static {
|
||||
sStoreClasses.put(HostAuth.LEGACY_SCHEME_IMAP, ImapStore.class);
|
||||
sStoreClasses.put(HostAuth.LEGACY_SCHEME_POP3, Pop3Store.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static named constructor. It should be overrode by extending class.
|
||||
* Because this method will be called through reflection, it can not be protected.
|
||||
|
@ -87,6 +82,9 @@ public abstract class Store {
|
|||
*/
|
||||
public synchronized static Store getInstance(Account account, Context context)
|
||||
throws MessagingException {
|
||||
if (sStores.isEmpty()) {
|
||||
sStoreClasses.put(context.getString(R.string.protocol_pop3), Pop3Store.class);
|
||||
}
|
||||
HostAuth hostAuth = account.getOrCreateHostAuthRecv(context);
|
||||
// An existing account might have been deleted
|
||||
if (hostAuth == null) return null;
|
||||
|
|
|
@ -1,520 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.mail.store.ImapStore.ImapException;
|
||||
import com.android.email.mail.store.imap.ImapConstants;
|
||||
import com.android.email.mail.store.imap.ImapList;
|
||||
import com.android.email.mail.store.imap.ImapResponse;
|
||||
import com.android.email.mail.store.imap.ImapResponseParser;
|
||||
import com.android.email.mail.store.imap.ImapUtility;
|
||||
import com.android.email.mail.transport.DiscourseLogger;
|
||||
import com.android.email.mail.transport.MailTransport;
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.mail.AuthenticationFailedException;
|
||||
import com.android.emailcommon.mail.CertificateValidationException;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.mail.Transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* A cacheable class that stores the details for a single IMAP connection.
|
||||
*/
|
||||
class ImapConnection {
|
||||
// Always check in FALSE
|
||||
private static final boolean DEBUG_FORCE_SEND_ID = false;
|
||||
|
||||
/** ID capability per RFC 2971*/
|
||||
public static final int CAPABILITY_ID = 1 << 0;
|
||||
/** NAMESPACE capability per RFC 2342 */
|
||||
public static final int CAPABILITY_NAMESPACE = 1 << 1;
|
||||
/** STARTTLS capability per RFC 3501 */
|
||||
public static final int CAPABILITY_STARTTLS = 1 << 2;
|
||||
/** UIDPLUS capability per RFC 4315 */
|
||||
public static final int CAPABILITY_UIDPLUS = 1 << 3;
|
||||
|
||||
/** The capabilities supported; a set of CAPABILITY_* values. */
|
||||
private int mCapabilities;
|
||||
private static final String IMAP_REDACTED_LOG = "[IMAP command redacted]";
|
||||
Transport mTransport;
|
||||
private ImapResponseParser mParser;
|
||||
private ImapStore mImapStore;
|
||||
private String mUsername;
|
||||
private String mLoginPhrase;
|
||||
private String mIdPhrase = null;
|
||||
/** # of command/response lines to log upon crash. */
|
||||
private static final int DISCOURSE_LOGGER_SIZE = 64;
|
||||
private final DiscourseLogger mDiscourse = new DiscourseLogger(DISCOURSE_LOGGER_SIZE);
|
||||
/**
|
||||
* Next tag to use. All connections associated to the same ImapStore instance share the same
|
||||
* counter to make tests simpler.
|
||||
* (Some of the tests involve multiple connections but only have a single counter to track the
|
||||
* tag.)
|
||||
*/
|
||||
private final AtomicInteger mNextCommandTag = new AtomicInteger(0);
|
||||
|
||||
|
||||
// Keep others from instantiating directly
|
||||
ImapConnection(ImapStore store, String username, String password) {
|
||||
setStore(store, username, password);
|
||||
}
|
||||
|
||||
void setStore(ImapStore store, String username, String password) {
|
||||
if (username != null && password != null) {
|
||||
mUsername = username;
|
||||
|
||||
// build the LOGIN string once (instead of over-and-over again.)
|
||||
// apply the quoting here around the built-up password
|
||||
mLoginPhrase = ImapConstants.LOGIN + " " + mUsername + " "
|
||||
+ ImapUtility.imapQuoted(password);
|
||||
}
|
||||
mImapStore = store;
|
||||
}
|
||||
void open() throws IOException, MessagingException {
|
||||
if (mTransport != null && mTransport.isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// copy configuration into a clean transport, if necessary
|
||||
if (mTransport == null) {
|
||||
mTransport = mImapStore.cloneTransport();
|
||||
}
|
||||
|
||||
mTransport.open();
|
||||
mTransport.setSoTimeout(MailTransport.SOCKET_READ_TIMEOUT);
|
||||
|
||||
createParser();
|
||||
|
||||
// BANNER
|
||||
mParser.readResponse();
|
||||
|
||||
// CAPABILITY
|
||||
ImapResponse capabilities = queryCapabilities();
|
||||
|
||||
boolean hasStartTlsCapability =
|
||||
capabilities.contains(ImapConstants.STARTTLS);
|
||||
|
||||
// TLS
|
||||
ImapResponse newCapabilities = doStartTls(hasStartTlsCapability);
|
||||
if (newCapabilities != null) {
|
||||
capabilities = newCapabilities;
|
||||
}
|
||||
|
||||
// NOTE: An IMAP response MUST be processed before issuing any new IMAP
|
||||
// requests. Subsequent requests may destroy previous response data. As
|
||||
// such, we save away capability information here for future use.
|
||||
setCapabilities(capabilities);
|
||||
String capabilityString = capabilities.flatten();
|
||||
|
||||
// ID
|
||||
doSendId(isCapable(CAPABILITY_ID), capabilityString);
|
||||
|
||||
// LOGIN
|
||||
doLogin();
|
||||
|
||||
// NAMESPACE (only valid in the Authenticated state)
|
||||
doGetNamespace(isCapable(CAPABILITY_NAMESPACE));
|
||||
|
||||
// Gets the path separator from the server
|
||||
doGetPathSeparator();
|
||||
|
||||
mImapStore.ensurePrefixIsValid();
|
||||
} catch (SSLException e) {
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, e.toString());
|
||||
}
|
||||
throw new CertificateValidationException(e.getMessage(), e);
|
||||
} catch (IOException ioe) {
|
||||
// NOTE: Unlike similar code in POP3, I'm going to rethrow as-is. There is a lot
|
||||
// of other code here that catches IOException and I don't want to break it.
|
||||
// This catch is only here to enhance logging of connection-time issues.
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, ioe.toString());
|
||||
}
|
||||
throw ioe;
|
||||
} finally {
|
||||
destroyResponses();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection and releases all resources. This connection can not be used again
|
||||
* until {@link #setStore(ImapStore, String, String)} is called.
|
||||
*/
|
||||
void close() {
|
||||
if (mTransport != null) {
|
||||
mTransport.close();
|
||||
mTransport = null;
|
||||
}
|
||||
destroyResponses();
|
||||
mParser = null;
|
||||
mImapStore = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the specified capability is supported by the server.
|
||||
*/
|
||||
private boolean isCapable(int capability) {
|
||||
return (mCapabilities & capability) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the capability flags according to the response provided by the server.
|
||||
* Note: We only set the capability flags that we are interested in. There are many IMAP
|
||||
* capabilities that we do not track.
|
||||
*/
|
||||
private void setCapabilities(ImapResponse capabilities) {
|
||||
if (capabilities.contains(ImapConstants.ID)) {
|
||||
mCapabilities |= CAPABILITY_ID;
|
||||
}
|
||||
if (capabilities.contains(ImapConstants.NAMESPACE)) {
|
||||
mCapabilities |= CAPABILITY_NAMESPACE;
|
||||
}
|
||||
if (capabilities.contains(ImapConstants.UIDPLUS)) {
|
||||
mCapabilities |= CAPABILITY_UIDPLUS;
|
||||
}
|
||||
if (capabilities.contains(ImapConstants.STARTTLS)) {
|
||||
mCapabilities |= CAPABILITY_STARTTLS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ImapResponseParser} from {@code mTransport.getInputStream()} and
|
||||
* set it to {@link #mParser}.
|
||||
*
|
||||
* If we already have an {@link ImapResponseParser}, we
|
||||
* {@link #destroyResponses()} and throw it away.
|
||||
*/
|
||||
private void createParser() {
|
||||
destroyResponses();
|
||||
mParser = new ImapResponseParser(mTransport.getInputStream(), mDiscourse);
|
||||
}
|
||||
|
||||
void destroyResponses() {
|
||||
if (mParser != null) {
|
||||
mParser.destroyResponses();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isTransportOpenForTest() {
|
||||
return mTransport != null ? mTransport.isOpen() : false;
|
||||
}
|
||||
|
||||
ImapResponse readResponse() throws IOException, MessagingException {
|
||||
return mParser.readResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a single command to the server. The command will be preceded by an IMAP command
|
||||
* tag and followed by \r\n (caller need not supply them).
|
||||
*
|
||||
* @param command The command to send to the server
|
||||
* @param sensitive If true, the command will not be logged
|
||||
* @return Returns the command tag that was sent
|
||||
*/
|
||||
String sendCommand(String command, boolean sensitive)
|
||||
throws MessagingException, IOException {
|
||||
open();
|
||||
String tag = Integer.toString(mNextCommandTag.incrementAndGet());
|
||||
String commandToSend = tag + " " + command;
|
||||
mTransport.writeLine(commandToSend, sensitive ? IMAP_REDACTED_LOG : null);
|
||||
mDiscourse.addSentCommand(sensitive ? IMAP_REDACTED_LOG : commandToSend);
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a single, complex command to the server. The command will be preceded by an IMAP
|
||||
* command tag and followed by \r\n (caller need not supply them). After each piece of the
|
||||
* command, a response will be read which MUST be a continuation request.
|
||||
*
|
||||
* @param commands An array of Strings comprising the command to be sent to the server
|
||||
* @return Returns the command tag that was sent
|
||||
*/
|
||||
String sendComplexCommand(List<String> commands, boolean sensitive) throws MessagingException,
|
||||
IOException {
|
||||
open();
|
||||
String tag = Integer.toString(mNextCommandTag.incrementAndGet());
|
||||
int len = commands.size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
String commandToSend = commands.get(i);
|
||||
// The first part of the command gets the tag
|
||||
if (i == 0) {
|
||||
commandToSend = tag + " " + commandToSend;
|
||||
} else {
|
||||
// Otherwise, read the response from the previous part of the command
|
||||
ImapResponse response = readResponse();
|
||||
// If it isn't a continuation request, that's an error
|
||||
if (!response.isContinuationRequest()) {
|
||||
throw new MessagingException("Expected continuation request");
|
||||
}
|
||||
}
|
||||
// Send the command
|
||||
mTransport.writeLine(commandToSend, null);
|
||||
mDiscourse.addSentCommand(sensitive ? IMAP_REDACTED_LOG : commandToSend);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
List<ImapResponse> executeSimpleCommand(String command) throws IOException,
|
||||
MessagingException {
|
||||
return executeSimpleCommand(command, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return all of the responses from the most recent command sent to the server
|
||||
*
|
||||
* @return a list of ImapResponses
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
*/
|
||||
List<ImapResponse> getCommandResponses() throws IOException, MessagingException {
|
||||
ArrayList<ImapResponse> responses = new ArrayList<ImapResponse>();
|
||||
ImapResponse response;
|
||||
do {
|
||||
response = mParser.readResponse();
|
||||
responses.add(response);
|
||||
} while (!response.isTagged());
|
||||
if (!response.isOk()) {
|
||||
final String toString = response.toString();
|
||||
final String alert = response.getAlertTextOrEmpty().getString();
|
||||
destroyResponses();
|
||||
throw new ImapException(toString, alert);
|
||||
}
|
||||
return responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a simple command at the server, a simple command being one that is sent in a single
|
||||
* line of text
|
||||
*
|
||||
* @param command the command to send to the server
|
||||
* @param sensitive whether the command should be redacted in logs (used for login)
|
||||
* @return a list of ImapResponses
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
*/
|
||||
List<ImapResponse> executeSimpleCommand(String command, boolean sensitive)
|
||||
throws IOException, MessagingException {
|
||||
sendCommand(command, sensitive);
|
||||
return getCommandResponses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a complex command at the server, a complex command being one that must be sent in
|
||||
* multiple lines due to the use of string literals
|
||||
*
|
||||
* @param commands a list of strings that comprise the command to be sent to the server
|
||||
* @param sensitive whether the command should be redacted in logs (used for login)
|
||||
* @return a list of ImapResponses
|
||||
* @throws IOException
|
||||
* @throws MessagingException
|
||||
*/
|
||||
List<ImapResponse> executeComplexCommand(List<String> commands, boolean sensitive)
|
||||
throws IOException, MessagingException {
|
||||
sendComplexCommand(commands, sensitive);
|
||||
return getCommandResponses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query server for capabilities.
|
||||
*/
|
||||
private ImapResponse queryCapabilities() throws IOException, MessagingException {
|
||||
ImapResponse capabilityResponse = null;
|
||||
for (ImapResponse r : executeSimpleCommand(ImapConstants.CAPABILITY)) {
|
||||
if (r.is(0, ImapConstants.CAPABILITY)) {
|
||||
capabilityResponse = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (capabilityResponse == null) {
|
||||
throw new MessagingException("Invalid CAPABILITY response received");
|
||||
}
|
||||
return capabilityResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends client identification information to the IMAP server per RFC 2971. If
|
||||
* the server does not support the ID command, this will perform no operation.
|
||||
*
|
||||
* Interoperability hack: Never send ID to *.secureserver.net, which sends back a
|
||||
* malformed response that our parser can't deal with.
|
||||
*/
|
||||
private void doSendId(boolean hasIdCapability, String capabilities)
|
||||
throws MessagingException {
|
||||
if (!hasIdCapability) return;
|
||||
|
||||
// Never send ID to *.secureserver.net
|
||||
String host = mTransport.getHost();
|
||||
if (host.toLowerCase().endsWith(".secureserver.net")) return;
|
||||
|
||||
// Assign user-agent string (for RFC2971 ID command)
|
||||
String mUserAgent =
|
||||
ImapStore.getImapId(mImapStore.getContext(), mUsername, host, capabilities);
|
||||
|
||||
if (mUserAgent != null) {
|
||||
mIdPhrase = ImapConstants.ID + " (" + mUserAgent + ")";
|
||||
} else if (DEBUG_FORCE_SEND_ID) {
|
||||
mIdPhrase = ImapConstants.ID + " " + ImapConstants.NIL;
|
||||
}
|
||||
// else: mIdPhrase = null, no ID will be emitted
|
||||
|
||||
// Send user-agent in an RFC2971 ID command
|
||||
if (mIdPhrase != null) {
|
||||
try {
|
||||
executeSimpleCommand(mIdPhrase);
|
||||
} catch (ImapException ie) {
|
||||
// Log for debugging, but this is not a fatal problem.
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, ie.toString());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// Special case to handle malformed OK responses and ignore them.
|
||||
// A true IOException will recur on the following login steps
|
||||
// This can go away after the parser is fixed - see bug 2138981
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's Personal Namespace from the IMAP server per RFC 2342. If the user
|
||||
* explicitly sets a namespace (using setup UI) or if the server does not support the
|
||||
* namespace command, this will perform no operation.
|
||||
*/
|
||||
private void doGetNamespace(boolean hasNamespaceCapability) throws MessagingException {
|
||||
// user did not specify a hard-coded prefix; try to get it from the server
|
||||
if (hasNamespaceCapability && !mImapStore.isUserPrefixSet()) {
|
||||
List<ImapResponse> responseList = Collections.emptyList();
|
||||
|
||||
try {
|
||||
responseList = executeSimpleCommand(ImapConstants.NAMESPACE);
|
||||
} catch (ImapException ie) {
|
||||
// Log for debugging, but this is not a fatal problem.
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, ie.toString());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// Special case to handle malformed OK responses and ignore them.
|
||||
}
|
||||
|
||||
for (ImapResponse response: responseList) {
|
||||
if (response.isDataResponse(0, ImapConstants.NAMESPACE)) {
|
||||
ImapList namespaceList = response.getListOrEmpty(1);
|
||||
ImapList namespace = namespaceList.getListOrEmpty(0);
|
||||
String namespaceString = namespace.getStringOrEmpty(0).getString();
|
||||
if (!TextUtils.isEmpty(namespaceString)) {
|
||||
mImapStore.setPathPrefix(ImapStore.decodeFolderName(namespaceString, null));
|
||||
mImapStore.setPathSeparator(namespace.getStringOrEmpty(1).getString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs into the IMAP server
|
||||
*/
|
||||
private void doLogin()
|
||||
throws IOException, MessagingException, AuthenticationFailedException {
|
||||
try {
|
||||
// TODO eventually we need to add additional authentication
|
||||
// options such as SASL
|
||||
executeSimpleCommand(mLoginPhrase, true);
|
||||
} catch (ImapException ie) {
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, ie.toString());
|
||||
}
|
||||
throw new AuthenticationFailedException(ie.getAlertText(), ie);
|
||||
|
||||
} catch (MessagingException me) {
|
||||
throw new AuthenticationFailedException(null, me);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path separator per the LIST command in RFC 3501. If the path separator
|
||||
* was obtained while obtaining the namespace or there is no prefix defined, this
|
||||
* will perform no operation.
|
||||
*/
|
||||
private void doGetPathSeparator() throws MessagingException {
|
||||
// user did not specify a hard-coded prefix; try to get it from the server
|
||||
if (mImapStore.isUserPrefixSet()) {
|
||||
List<ImapResponse> responseList = Collections.emptyList();
|
||||
|
||||
try {
|
||||
responseList = executeSimpleCommand(ImapConstants.LIST + " \"\" \"\"");
|
||||
} catch (ImapException ie) {
|
||||
// Log for debugging, but this is not a fatal problem.
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, ie.toString());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// Special case to handle malformed OK responses and ignore them.
|
||||
}
|
||||
|
||||
for (ImapResponse response: responseList) {
|
||||
if (response.isDataResponse(0, ImapConstants.LIST)) {
|
||||
mImapStore.setPathSeparator(response.getStringOrEmpty(2).getString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a TLS session with the IMAP server per RFC 3501. If the user has not opted
|
||||
* to use TLS or the server does not support the TLS capability, this will perform
|
||||
* no operation.
|
||||
*/
|
||||
private ImapResponse doStartTls(boolean hasStartTlsCapability)
|
||||
throws IOException, MessagingException {
|
||||
if (mTransport.canTryTlsSecurity()) {
|
||||
if (hasStartTlsCapability) {
|
||||
// STARTTLS
|
||||
executeSimpleCommand(ImapConstants.STARTTLS);
|
||||
|
||||
mTransport.reopenTls();
|
||||
mTransport.setSoTimeout(MailTransport.SOCKET_READ_TIMEOUT);
|
||||
createParser();
|
||||
// Per RFC requirement (3501-6.2.1) gather new capabilities
|
||||
return(queryCapabilities());
|
||||
} else {
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, "TLS not supported but required");
|
||||
}
|
||||
throw new MessagingException(MessagingException.TLS_REQUIRED);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @see DiscourseLogger#logLastDiscourse() */
|
||||
void logLastDiscourse() {
|
||||
mDiscourse.logLastDiscourse();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,618 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.LegacyConversions;
|
||||
import com.android.email.Preferences;
|
||||
import com.android.email.R;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.mail.store.imap.ImapConstants;
|
||||
import com.android.email.mail.store.imap.ImapResponse;
|
||||
import com.android.email.mail.store.imap.ImapString;
|
||||
import com.android.email.mail.transport.MailTransport;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.VendorPolicyLoader;
|
||||
import com.android.emailcommon.internet.MimeMessage;
|
||||
import com.android.emailcommon.mail.AuthenticationFailedException;
|
||||
import com.android.emailcommon.mail.Flag;
|
||||
import com.android.emailcommon.mail.Folder;
|
||||
import com.android.emailcommon.mail.Message;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.mail.Transport;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
import com.android.emailcommon.service.EmailServiceProxy;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
import com.beetstra.jutf7.CharsetProvider;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* TODO Need to start keeping track of UIDVALIDITY
|
||||
* TODO Need a default response handler for things like folder updates
|
||||
* TODO In fetch(), if we need a ImapMessage and were given
|
||||
* something else we can try to do a pre-fetch first.
|
||||
* TODO Collect ALERT messages and show them to users.
|
||||
*
|
||||
* ftp://ftp.isi.edu/in-notes/rfc2683.txt When a client asks for
|
||||
* certain information in a FETCH command, the server may return the requested
|
||||
* information in any order, not necessarily in the order that it was requested.
|
||||
* Further, the server may return the information in separate FETCH responses
|
||||
* and may also return information that was not explicitly requested (to reflect
|
||||
* to the client changes in the state of the subject message).
|
||||
* </pre>
|
||||
*/
|
||||
public class ImapStore extends Store {
|
||||
/** Charset used for converting folder names to and from UTF-7 as defined by RFC 3501. */
|
||||
private static final Charset MODIFIED_UTF_7_CHARSET =
|
||||
new CharsetProvider().charsetForName("X-RFC-3501");
|
||||
|
||||
@VisibleForTesting static String sImapId = null;
|
||||
@VisibleForTesting String mPathPrefix;
|
||||
@VisibleForTesting String mPathSeparator;
|
||||
|
||||
private final ConcurrentLinkedQueue<ImapConnection> mConnectionPool =
|
||||
new ConcurrentLinkedQueue<ImapConnection>();
|
||||
|
||||
/**
|
||||
* Static named constructor.
|
||||
*/
|
||||
public static Store newInstance(Account account, Context context) throws MessagingException {
|
||||
return new ImapStore(context, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new store for the given account. Always use
|
||||
* {@link #newInstance(Account, Context)} to create an IMAP store.
|
||||
*/
|
||||
private ImapStore(Context context, Account account) throws MessagingException {
|
||||
mContext = context;
|
||||
mAccount = account;
|
||||
|
||||
HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
|
||||
if (recvAuth == null || !HostAuth.LEGACY_SCHEME_IMAP.equalsIgnoreCase(recvAuth.mProtocol)) {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
mTransport = new MailTransport(context, "IMAP", recvAuth);
|
||||
|
||||
String[] userInfo = recvAuth.getLogin();
|
||||
if (userInfo != null) {
|
||||
mUsername = userInfo[0];
|
||||
mPassword = userInfo[1];
|
||||
} else {
|
||||
mUsername = null;
|
||||
mPassword = null;
|
||||
}
|
||||
mPathPrefix = recvAuth.mDomain;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Collection<ImapConnection> getConnectionPoolForTest() {
|
||||
return mConnectionPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing only. Injects a different root transport (it will be copied using
|
||||
* newInstanceWithConfiguration() each time IMAP sets up a new channel). The transport
|
||||
* should already be set up and ready to use. Do not use for real code.
|
||||
* @param testTransport The Transport to inject and use for all future communication.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setTransportForTest(Transport testTransport) {
|
||||
mTransport = testTransport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return, or create and return, an string suitable for use in an IMAP ID message.
|
||||
* This is constructed similarly to the way the browser sets up its user-agent strings.
|
||||
* See RFC 2971 for more details. The output of this command will be a series of key-value
|
||||
* pairs delimited by spaces (there is no point in returning a structured result because
|
||||
* this will be sent as-is to the IMAP server). No tokens, parenthesis or "ID" are included,
|
||||
* because some connections may append additional values.
|
||||
*
|
||||
* The following IMAP ID keys may be included:
|
||||
* name Android package name of the program
|
||||
* os "android"
|
||||
* os-version "version; model; build-id"
|
||||
* vendor Vendor of the client/server
|
||||
* x-android-device-model Model (only revealed if release build)
|
||||
* x-android-net-operator Mobile network operator (if known)
|
||||
* AGUID A device+account UID
|
||||
*
|
||||
* In addition, a vendor policy .apk can append key/value pairs.
|
||||
*
|
||||
* @param userName the username of the account
|
||||
* @param host the host (server) of the account
|
||||
* @param capabilities a list of the capabilities from the server
|
||||
* @return a String for use in an IMAP ID message.
|
||||
*/
|
||||
public static String getImapId(Context context, String userName, String host,
|
||||
String capabilities) {
|
||||
// The first section is global to all IMAP connections, and generates the fixed
|
||||
// values in any IMAP ID message
|
||||
synchronized (ImapStore.class) {
|
||||
if (sImapId == null) {
|
||||
TelephonyManager tm =
|
||||
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String networkOperator = tm.getNetworkOperatorName();
|
||||
if (networkOperator == null) networkOperator = "";
|
||||
|
||||
sImapId = makeCommonImapId(context.getPackageName(), Build.VERSION.RELEASE,
|
||||
Build.VERSION.CODENAME, Build.MODEL, Build.ID, Build.MANUFACTURER,
|
||||
networkOperator);
|
||||
}
|
||||
}
|
||||
|
||||
// This section is per Store, and adds in a dynamic elements like UID's.
|
||||
// We don't cache the result of this work, because the caller does anyway.
|
||||
StringBuilder id = new StringBuilder(sImapId);
|
||||
|
||||
// Optionally add any vendor-supplied id keys
|
||||
String vendorId = VendorPolicyLoader.getInstance(context).getImapIdValues(userName, host,
|
||||
capabilities);
|
||||
if (vendorId != null) {
|
||||
id.append(' ');
|
||||
id.append(vendorId);
|
||||
}
|
||||
|
||||
// Generate a UID that mixes a "stable" device UID with the email address
|
||||
try {
|
||||
String devUID = Preferences.getPreferences(context).getDeviceUID();
|
||||
MessageDigest messageDigest;
|
||||
messageDigest = MessageDigest.getInstance("SHA-1");
|
||||
messageDigest.update(userName.getBytes());
|
||||
messageDigest.update(devUID.getBytes());
|
||||
byte[] uid = messageDigest.digest();
|
||||
String hexUid = Base64.encodeToString(uid, Base64.NO_WRAP);
|
||||
id.append(" \"AGUID\" \"");
|
||||
id.append(hexUid);
|
||||
id.append('\"');
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(Logging.LOG_TAG, "couldn't obtain SHA-1 hash for device UID");
|
||||
}
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that actually builds the static part of the IMAP ID string. This is
|
||||
* separated from getImapId for testability. There is no escaping or encoding in IMAP ID so
|
||||
* any rogue chars must be filtered here.
|
||||
*
|
||||
* @param packageName context.getPackageName()
|
||||
* @param version Build.VERSION.RELEASE
|
||||
* @param codeName Build.VERSION.CODENAME
|
||||
* @param model Build.MODEL
|
||||
* @param id Build.ID
|
||||
* @param vendor Build.MANUFACTURER
|
||||
* @param networkOperator TelephonyManager.getNetworkOperatorName()
|
||||
* @return the static (never changes) portion of the IMAP ID
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static String makeCommonImapId(String packageName, String version,
|
||||
String codeName, String model, String id, String vendor, String networkOperator) {
|
||||
|
||||
// Before building up IMAP ID string, pre-filter the input strings for "legal" chars
|
||||
// This is using a fairly arbitrary char set intended to pass through most reasonable
|
||||
// version, model, and vendor strings: a-z A-Z 0-9 - _ + = ; : . , / <space>
|
||||
// The most important thing is *not* to pass parens, quotes, or CRLF, which would break
|
||||
// the format of the IMAP ID list.
|
||||
Pattern p = Pattern.compile("[^a-zA-Z0-9-_\\+=;:\\.,/ ]");
|
||||
packageName = p.matcher(packageName).replaceAll("");
|
||||
version = p.matcher(version).replaceAll("");
|
||||
codeName = p.matcher(codeName).replaceAll("");
|
||||
model = p.matcher(model).replaceAll("");
|
||||
id = p.matcher(id).replaceAll("");
|
||||
vendor = p.matcher(vendor).replaceAll("");
|
||||
networkOperator = p.matcher(networkOperator).replaceAll("");
|
||||
|
||||
// "name" "com.android.email"
|
||||
StringBuffer sb = new StringBuffer("\"name\" \"");
|
||||
sb.append(packageName);
|
||||
sb.append("\"");
|
||||
|
||||
// "os" "android"
|
||||
sb.append(" \"os\" \"android\"");
|
||||
|
||||
// "os-version" "version; build-id"
|
||||
sb.append(" \"os-version\" \"");
|
||||
if (version.length() > 0) {
|
||||
sb.append(version);
|
||||
} else {
|
||||
// default to "1.0"
|
||||
sb.append("1.0");
|
||||
}
|
||||
// add the build ID or build #
|
||||
if (id.length() > 0) {
|
||||
sb.append("; ");
|
||||
sb.append(id);
|
||||
}
|
||||
sb.append("\"");
|
||||
|
||||
// "vendor" "the vendor"
|
||||
if (vendor.length() > 0) {
|
||||
sb.append(" \"vendor\" \"");
|
||||
sb.append(vendor);
|
||||
sb.append("\"");
|
||||
}
|
||||
|
||||
// "x-android-device-model" the device model (on release builds only)
|
||||
if ("REL".equals(codeName)) {
|
||||
if (model.length() > 0) {
|
||||
sb.append(" \"x-android-device-model\" \"");
|
||||
sb.append(model);
|
||||
sb.append("\"");
|
||||
}
|
||||
}
|
||||
|
||||
// "x-android-mobile-net-operator" "name of network operator"
|
||||
if (networkOperator.length() > 0) {
|
||||
sb.append(" \"x-android-mobile-net-operator\" \"");
|
||||
sb.append(networkOperator);
|
||||
sb.append("\"");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Folder getFolder(String name) {
|
||||
return new ImapFolder(this, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mailbox hierarchy out of the flat data provided by the server.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static void createHierarchy(HashMap<String, ImapFolder> mailboxes) {
|
||||
Set<String> pathnames = mailboxes.keySet();
|
||||
for (String path : pathnames) {
|
||||
final ImapFolder folder = mailboxes.get(path);
|
||||
final Mailbox mailbox = folder.mMailbox;
|
||||
int delimiterIdx = mailbox.mServerId.lastIndexOf(mailbox.mDelimiter);
|
||||
long parentKey = Mailbox.NO_MAILBOX;
|
||||
if (delimiterIdx != -1) {
|
||||
String parentPath = path.substring(0, delimiterIdx);
|
||||
final ImapFolder parentFolder = mailboxes.get(parentPath);
|
||||
final Mailbox parentMailbox = (parentFolder == null) ? null : parentFolder.mMailbox;
|
||||
if (parentMailbox != null) {
|
||||
parentKey = parentMailbox.mId;
|
||||
parentMailbox.mFlags
|
||||
|= (Mailbox.FLAG_HAS_CHILDREN | Mailbox.FLAG_CHILDREN_VISIBLE);
|
||||
}
|
||||
}
|
||||
mailbox.mParentKey = parentKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Folder} and associated {@link Mailbox}. If the folder does not already
|
||||
* exist in the local database, a new row will immediately be created in the mailbox table.
|
||||
* Otherwise, the existing row will be used. Any changes to existing rows, will not be stored
|
||||
* to the database immediately.
|
||||
* @param accountId The ID of the account the mailbox is to be associated with
|
||||
* @param mailboxPath The path of the mailbox to add
|
||||
* @param delimiter A path delimiter. May be {@code null} if there is no delimiter.
|
||||
* @param selectable If {@code true}, the mailbox can be selected and used to store messages.
|
||||
*/
|
||||
private ImapFolder addMailbox(Context context, long accountId, String mailboxPath,
|
||||
char delimiter, boolean selectable) {
|
||||
ImapFolder folder = (ImapFolder) getFolder(mailboxPath);
|
||||
Mailbox mailbox = Mailbox.getMailboxForPath(context, accountId, mailboxPath);
|
||||
if (mailbox.isSaved()) {
|
||||
// existing mailbox
|
||||
// mailbox retrieved from database; save hash _before_ updating fields
|
||||
folder.mHash = mailbox.getHashes();
|
||||
}
|
||||
updateMailbox(mailbox, accountId, mailboxPath, delimiter, selectable,
|
||||
LegacyConversions.inferMailboxTypeFromName(context, mailboxPath));
|
||||
if (folder.mHash == null) {
|
||||
// new mailbox
|
||||
// save hash after updating. allows tracking changes if the mailbox is saved
|
||||
// outside of #saveMailboxList()
|
||||
folder.mHash = mailbox.getHashes();
|
||||
// We must save this here to make sure we have a valid ID for later
|
||||
mailbox.save(mContext);
|
||||
}
|
||||
folder.mMailbox = mailbox;
|
||||
return folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the folders in the given list.
|
||||
*/
|
||||
private static void saveMailboxList(Context context, HashMap<String, ImapFolder> folderMap) {
|
||||
for (ImapFolder imapFolder : folderMap.values()) {
|
||||
imapFolder.save(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Folder[] updateFolders() throws MessagingException {
|
||||
ImapConnection connection = getConnection();
|
||||
try {
|
||||
HashMap<String, ImapFolder> mailboxes = new HashMap<String, ImapFolder>();
|
||||
// Establish a connection to the IMAP server; if necessary
|
||||
// This ensures a valid prefix if the prefix is automatically set by the server
|
||||
connection.executeSimpleCommand(ImapConstants.NOOP);
|
||||
String imapCommand = ImapConstants.LIST + " \"\" \"*\"";
|
||||
if (mPathPrefix != null) {
|
||||
imapCommand = ImapConstants.LIST + " \"\" \"" + mPathPrefix + "*\"";
|
||||
}
|
||||
List<ImapResponse> responses = connection.executeSimpleCommand(imapCommand);
|
||||
for (ImapResponse response : responses) {
|
||||
// S: * LIST (\Noselect) "/" ~/Mail/foo
|
||||
if (response.isDataResponse(0, ImapConstants.LIST)) {
|
||||
// Get folder name.
|
||||
ImapString encodedFolder = response.getStringOrEmpty(3);
|
||||
if (encodedFolder.isEmpty()) continue;
|
||||
|
||||
String folderName = decodeFolderName(encodedFolder.getString(), mPathPrefix);
|
||||
if (ImapConstants.INBOX.equalsIgnoreCase(folderName)) continue;
|
||||
|
||||
// Parse attributes.
|
||||
boolean selectable =
|
||||
!response.getListOrEmpty(1).contains(ImapConstants.FLAG_NO_SELECT);
|
||||
String delimiter = response.getStringOrEmpty(2).getString();
|
||||
char delimiterChar = '\0';
|
||||
if (!TextUtils.isEmpty(delimiter)) {
|
||||
delimiterChar = delimiter.charAt(0);
|
||||
}
|
||||
ImapFolder folder =
|
||||
addMailbox(mContext, mAccount.mId, folderName, delimiterChar, selectable);
|
||||
mailboxes.put(folderName, folder);
|
||||
}
|
||||
}
|
||||
String inboxName = mContext.getString(R.string.mailbox_name_display_inbox);
|
||||
Folder newFolder =
|
||||
addMailbox(mContext, mAccount.mId, inboxName, '\0', true /*selectable*/);
|
||||
mailboxes.put(ImapConstants.INBOX, (ImapFolder)newFolder);
|
||||
createHierarchy(mailboxes);
|
||||
saveMailboxList(mContext, mailboxes);
|
||||
return mailboxes.values().toArray(new Folder[] {});
|
||||
} catch (IOException ioe) {
|
||||
connection.close();
|
||||
throw new MessagingException("Unable to get folder list.", ioe);
|
||||
} catch (AuthenticationFailedException afe) {
|
||||
// We do NOT want this connection pooled, or we will continue to send NOOP and SELECT
|
||||
// commands to the server
|
||||
connection.destroyResponses();
|
||||
connection = null;
|
||||
throw afe;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
poolConnection(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle checkSettings() throws MessagingException {
|
||||
int result = MessagingException.NO_ERROR;
|
||||
Bundle bundle = new Bundle();
|
||||
ImapConnection connection = new ImapConnection(this, mUsername, mPassword);
|
||||
try {
|
||||
connection.open();
|
||||
connection.close();
|
||||
} catch (IOException ioe) {
|
||||
bundle.putString(EmailServiceProxy.VALIDATE_BUNDLE_ERROR_MESSAGE, ioe.getMessage());
|
||||
result = MessagingException.IOERROR;
|
||||
} finally {
|
||||
connection.destroyResponses();
|
||||
}
|
||||
bundle.putInt(EmailServiceProxy.VALIDATE_BUNDLE_RESULT_CODE, result);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the prefix has been set by the user. This can be determined by
|
||||
* the fact that the prefix is set, but, the path separator is not set.
|
||||
*/
|
||||
boolean isUserPrefixSet() {
|
||||
return TextUtils.isEmpty(mPathSeparator) && !TextUtils.isEmpty(mPathPrefix);
|
||||
}
|
||||
|
||||
/** Sets the path separator */
|
||||
void setPathSeparator(String pathSeparator) {
|
||||
mPathSeparator = pathSeparator;
|
||||
}
|
||||
|
||||
/** Sets the prefix */
|
||||
void setPathPrefix(String pathPrefix) {
|
||||
mPathPrefix = pathPrefix;
|
||||
}
|
||||
|
||||
/** Gets the context for this store */
|
||||
Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
/** Returns a clone of the transport associated with this store. */
|
||||
Transport cloneTransport() {
|
||||
return mTransport.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the path prefix, if necessary. The path prefix must always end with the
|
||||
* path separator.
|
||||
*/
|
||||
void ensurePrefixIsValid() {
|
||||
// Make sure the path prefix ends with the path separator
|
||||
if (!TextUtils.isEmpty(mPathPrefix) && !TextUtils.isEmpty(mPathSeparator)) {
|
||||
if (!mPathPrefix.endsWith(mPathSeparator)) {
|
||||
mPathPrefix = mPathPrefix + mPathSeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a connection if one is available from the pool, or creates a new one if not.
|
||||
*/
|
||||
ImapConnection getConnection() {
|
||||
ImapConnection connection = null;
|
||||
while ((connection = mConnectionPool.poll()) != null) {
|
||||
try {
|
||||
connection.setStore(this, mUsername, mPassword);
|
||||
connection.executeSimpleCommand(ImapConstants.NOOP);
|
||||
break;
|
||||
} catch (MessagingException e) {
|
||||
// Fall through
|
||||
} catch (IOException e) {
|
||||
// Fall through
|
||||
}
|
||||
connection.close();
|
||||
connection = null;
|
||||
}
|
||||
if (connection == null) {
|
||||
connection = new ImapConnection(this, mUsername, mPassword);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a {@link ImapConnection} in the pool for reuse. Any responses associated with the
|
||||
* connection are destroyed before adding the connection to the pool.
|
||||
*/
|
||||
void poolConnection(ImapConnection connection) {
|
||||
if (connection != null) {
|
||||
connection.destroyResponses();
|
||||
mConnectionPool.add(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the folder name with the given prefix and UTF-7 encodes it.
|
||||
*/
|
||||
static String encodeFolderName(String name, String prefix) {
|
||||
// do NOT add the prefix to the special name "INBOX"
|
||||
if (ImapConstants.INBOX.equalsIgnoreCase(name)) return name;
|
||||
|
||||
// Prepend prefix
|
||||
if (prefix != null) {
|
||||
name = prefix + name;
|
||||
}
|
||||
|
||||
// TODO bypass the conversion if name doesn't have special char.
|
||||
ByteBuffer bb = MODIFIED_UTF_7_CHARSET.encode(name);
|
||||
byte[] b = new byte[bb.limit()];
|
||||
bb.get(b);
|
||||
|
||||
return Utility.fromAscii(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-7 decodes the folder name and removes the given path prefix.
|
||||
*/
|
||||
static String decodeFolderName(String name, String prefix) {
|
||||
// TODO bypass the conversion if name doesn't have special char.
|
||||
String folder;
|
||||
folder = MODIFIED_UTF_7_CHARSET.decode(ByteBuffer.wrap(Utility.toAscii(name))).toString();
|
||||
if ((prefix != null) && folder.startsWith(prefix)) {
|
||||
folder = folder.substring(prefix.length());
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns UIDs of Messages joined with "," as the separator.
|
||||
*/
|
||||
static String joinMessageUids(Message[] messages) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean notFirst = false;
|
||||
for (Message m : messages) {
|
||||
if (notFirst) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(m.getUid());
|
||||
notFirst = true;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static class ImapMessage extends MimeMessage {
|
||||
ImapMessage(String uid, ImapFolder folder) {
|
||||
mUid = uid;
|
||||
mFolder = folder;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(InputStream in) throws IOException, MessagingException {
|
||||
super.parse(in);
|
||||
}
|
||||
|
||||
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlag(Flag flag, boolean set) throws MessagingException {
|
||||
super.setFlag(flag, set);
|
||||
mFolder.setFlags(new Message[] { this }, new Flag[] { flag }, set);
|
||||
}
|
||||
}
|
||||
|
||||
static class ImapException extends MessagingException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
String mAlertText;
|
||||
|
||||
public ImapException(String message, String alertText, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
mAlertText = alertText;
|
||||
}
|
||||
|
||||
public ImapException(String message, String alertText) {
|
||||
super(message);
|
||||
mAlertText = alertText;
|
||||
}
|
||||
|
||||
public String getAlertText() {
|
||||
return mAlertText;
|
||||
}
|
||||
|
||||
public void setAlertText(String alertText) {
|
||||
mAlertText = alertText;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,10 +30,10 @@ import com.android.emailcommon.mail.AuthenticationFailedException;
|
|||
import com.android.emailcommon.mail.FetchProfile;
|
||||
import com.android.emailcommon.mail.Flag;
|
||||
import com.android.emailcommon.mail.Folder;
|
||||
import com.android.emailcommon.mail.Transport;
|
||||
import com.android.emailcommon.mail.Folder.OpenMode;
|
||||
import com.android.emailcommon.mail.Message;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.mail.Transport;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
|
@ -77,9 +77,6 @@ public class Pop3Store extends Store {
|
|||
mAccount = account;
|
||||
|
||||
HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
|
||||
if (recvAuth == null || !HostAuth.LEGACY_SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) {
|
||||
throw new MessagingException("Unsupported protocol");
|
||||
}
|
||||
mTransport = new MailTransport(context, "POP3", recvAuth);
|
||||
String[] userInfoParts = recvAuth.getLogin();
|
||||
if (userInfoParts != null) {
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import com.android.email.mail.Store;
|
||||
|
||||
public final class ImapConstants {
|
||||
private ImapConstants() {}
|
||||
|
||||
public static final String FETCH_FIELD_BODY_PEEK_BARE = "BODY.PEEK";
|
||||
public static final String FETCH_FIELD_BODY_PEEK = FETCH_FIELD_BODY_PEEK_BARE + "[]";
|
||||
public static final String FETCH_FIELD_BODY_PEEK_SANE
|
||||
= String.format("BODY.PEEK[]<0.%d>", Store.FETCH_BODY_SANE_SUGGESTED_SIZE);
|
||||
public static final String FETCH_FIELD_HEADERS =
|
||||
"BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc message-id)]";
|
||||
|
||||
public static final String ALERT = "ALERT";
|
||||
public static final String APPEND = "APPEND";
|
||||
public static final String BAD = "BAD";
|
||||
public static final String BADCHARSET = "BADCHARSET";
|
||||
public static final String BODY = "BODY";
|
||||
public static final String BODY_BRACKET_HEADER = "BODY[HEADER";
|
||||
public static final String BODYSTRUCTURE = "BODYSTRUCTURE";
|
||||
public static final String BYE = "BYE";
|
||||
public static final String CAPABILITY = "CAPABILITY";
|
||||
public static final String CHECK = "CHECK";
|
||||
public static final String CLOSE = "CLOSE";
|
||||
public static final String COPY = "COPY";
|
||||
public static final String COPYUID = "COPYUID";
|
||||
public static final String CREATE = "CREATE";
|
||||
public static final String DELETE = "DELETE";
|
||||
public static final String EXAMINE = "EXAMINE";
|
||||
public static final String EXISTS = "EXISTS";
|
||||
public static final String EXPUNGE = "EXPUNGE";
|
||||
public static final String FETCH = "FETCH";
|
||||
public static final String FLAG_ANSWERED = "\\ANSWERED";
|
||||
public static final String FLAG_DELETED = "\\DELETED";
|
||||
public static final String FLAG_FLAGGED = "\\FLAGGED";
|
||||
public static final String FLAG_NO_SELECT = "\\NOSELECT";
|
||||
public static final String FLAG_SEEN = "\\SEEN";
|
||||
public static final String FLAGS = "FLAGS";
|
||||
public static final String FLAGS_SILENT = "FLAGS.SILENT";
|
||||
public static final String ID = "ID";
|
||||
public static final String INBOX = "INBOX";
|
||||
public static final String INTERNALDATE = "INTERNALDATE";
|
||||
public static final String LIST = "LIST";
|
||||
public static final String LOGIN = "LOGIN";
|
||||
public static final String LOGOUT = "LOGOUT";
|
||||
public static final String LSUB = "LSUB";
|
||||
public static final String NAMESPACE = "NAMESPACE";
|
||||
public static final String NO = "NO";
|
||||
public static final String NOOP = "NOOP";
|
||||
public static final String OK = "OK";
|
||||
public static final String PARSE = "PARSE";
|
||||
public static final String PERMANENTFLAGS = "PERMANENTFLAGS";
|
||||
public static final String PREAUTH = "PREAUTH";
|
||||
public static final String READ_ONLY = "READ-ONLY";
|
||||
public static final String READ_WRITE = "READ-WRITE";
|
||||
public static final String RENAME = "RENAME";
|
||||
public static final String RFC822_SIZE = "RFC822.SIZE";
|
||||
public static final String SEARCH = "SEARCH";
|
||||
public static final String SELECT = "SELECT";
|
||||
public static final String STARTTLS = "STARTTLS";
|
||||
public static final String STATUS = "STATUS";
|
||||
public static final String STORE = "STORE";
|
||||
public static final String SUBSCRIBE = "SUBSCRIBE";
|
||||
public static final String TEXT = "TEXT";
|
||||
public static final String TRYCREATE = "TRYCREATE";
|
||||
public static final String UID = "UID";
|
||||
public static final String UID_COPY = "UID COPY";
|
||||
public static final String UID_FETCH = "UID FETCH";
|
||||
public static final String UID_SEARCH = "UID SEARCH";
|
||||
public static final String UID_STORE = "UID STORE";
|
||||
public static final String UIDNEXT = "UIDNEXT";
|
||||
public static final String UIDPLUS = "UIDPLUS";
|
||||
public static final String UIDVALIDITY = "UIDVALIDITY";
|
||||
public static final String UNSEEN = "UNSEEN";
|
||||
public static final String UNSUBSCRIBE = "UNSUBSCRIBE";
|
||||
public static final String APPENDUID = "APPENDUID";
|
||||
public static final String NIL = "NIL";
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
/**
|
||||
* Class representing "element"s in IMAP responses.
|
||||
*
|
||||
* <p>Class hierarchy:
|
||||
* <pre>
|
||||
* ImapElement
|
||||
* |
|
||||
* |-- ImapElement.NONE (for 'index out of range')
|
||||
* |
|
||||
* |-- ImapList (isList() == true)
|
||||
* | |
|
||||
* | |-- ImapList.EMPTY
|
||||
* | |
|
||||
* | --- ImapResponse
|
||||
* |
|
||||
* --- ImapString (isString() == true)
|
||||
* |
|
||||
* |-- ImapString.EMPTY
|
||||
* |
|
||||
* |-- ImapSimpleString
|
||||
* |
|
||||
* |-- ImapMemoryLiteral
|
||||
* |
|
||||
* --- ImapTempFileLiteral
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class ImapElement {
|
||||
/**
|
||||
* An element that is returned by {@link ImapList#getElementOrNone} to indicate an index
|
||||
* is out of range.
|
||||
*/
|
||||
public static final ImapElement NONE = new ImapElement() {
|
||||
@Override public void destroy() {
|
||||
// Don't call super.destroy().
|
||||
// It's a shared object. We don't want the mDestroyed to be set on this.
|
||||
}
|
||||
|
||||
@Override public boolean isList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean isString() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "[NO ELEMENT]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsForTest(ImapElement that) {
|
||||
return super.equalsForTest(that);
|
||||
}
|
||||
};
|
||||
|
||||
private boolean mDestroyed = false;
|
||||
|
||||
public abstract boolean isList();
|
||||
|
||||
public abstract boolean isString();
|
||||
|
||||
protected boolean isDestroyed() {
|
||||
return mDestroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the resources used by the instance.
|
||||
* It's for removing a temp file used by {@link ImapTempFileLiteral}.
|
||||
*/
|
||||
public void destroy() {
|
||||
mDestroyed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws {@link RuntimeException} if it's already destroyed.
|
||||
*/
|
||||
protected final void checkNotDestroyed() {
|
||||
if (mDestroyed) {
|
||||
throw new RuntimeException("Already destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string that represents this object; it's purely for the debug purpose. Don't
|
||||
* mistake it for {@link ImapString#getString}.
|
||||
*
|
||||
* Abstract to force subclasses to implement it.
|
||||
*/
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
/**
|
||||
* The equals implementation that is intended to be used only for unit testing.
|
||||
* (Because it may be heavy and has a special sense of "equal" for testing.)
|
||||
*/
|
||||
public boolean equalsForTest(ImapElement that) {
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
return this.getClass() == that.getClass(); // Has to be the same class.
|
||||
}
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class represents an IMAP list.
|
||||
*/
|
||||
public class ImapList extends ImapElement {
|
||||
/**
|
||||
* {@link ImapList} representing an empty list.
|
||||
*/
|
||||
public static final ImapList EMPTY = new ImapList() {
|
||||
@Override public void destroy() {
|
||||
// Don't call super.destroy().
|
||||
// It's a shared object. We don't want the mDestroyed to be set on this.
|
||||
}
|
||||
|
||||
@Override void add(ImapElement e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
};
|
||||
|
||||
private ArrayList<ImapElement> mList = new ArrayList<ImapElement>();
|
||||
|
||||
/* package */ void add(ImapElement e) {
|
||||
if (e == null) {
|
||||
throw new RuntimeException("Can't add null");
|
||||
}
|
||||
mList.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isString() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final int size() {
|
||||
return mList.size();
|
||||
}
|
||||
|
||||
public final boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the element at {@code index} exists, is string, and equals to {@code s}.
|
||||
* (case insensitive)
|
||||
*/
|
||||
public final boolean is(int index, String s) {
|
||||
return is(index, s, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #is(int, String)}, but does the prefix match if {@code prefixMatch}.
|
||||
*/
|
||||
public final boolean is(int index, String s, boolean prefixMatch) {
|
||||
if (!prefixMatch) {
|
||||
return getStringOrEmpty(index).is(s);
|
||||
} else {
|
||||
return getStringOrEmpty(index).startsWith(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element at {@code index}.
|
||||
* If {@code index} is out of range, returns {@link ImapElement#NONE}.
|
||||
*/
|
||||
public final ImapElement getElementOrNone(int index) {
|
||||
return (index >= mList.size()) ? ImapElement.NONE : mList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element at {@code index} if it's a list.
|
||||
* If {@code index} is out of range or not a list, returns {@link ImapList#EMPTY}.
|
||||
*/
|
||||
public final ImapList getListOrEmpty(int index) {
|
||||
ImapElement el = getElementOrNone(index);
|
||||
return el.isList() ? (ImapList) el : EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element at {@code index} if it's a string.
|
||||
* If {@code index} is out of range or not a string, returns {@link ImapString#EMPTY}.
|
||||
*/
|
||||
public final ImapString getStringOrEmpty(int index) {
|
||||
ImapElement el = getElementOrNone(index);
|
||||
return el.isString() ? (ImapString) el : ImapString.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an element keyed by {@code key}. Return null if not found. {@code key} has to be
|
||||
* at an even index.
|
||||
*/
|
||||
/* package */ final ImapElement getKeyedElementOrNull(String key, boolean prefixMatch) {
|
||||
for (int i = 1; i < size(); i += 2) {
|
||||
if (is(i-1, key, prefixMatch)) {
|
||||
return mList.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link ImapList} keyed by {@code key}.
|
||||
* Return {@link ImapList#EMPTY} if not found.
|
||||
*/
|
||||
public final ImapList getKeyedListOrEmpty(String key) {
|
||||
return getKeyedListOrEmpty(key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link ImapList} keyed by {@code key}.
|
||||
* Return {@link ImapList#EMPTY} if not found.
|
||||
*/
|
||||
public final ImapList getKeyedListOrEmpty(String key, boolean prefixMatch) {
|
||||
ImapElement e = getKeyedElementOrNull(key, prefixMatch);
|
||||
return (e != null) ? ((ImapList) e) : ImapList.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link ImapString} keyed by {@code key}.
|
||||
* Return {@link ImapString#EMPTY} if not found.
|
||||
*/
|
||||
public final ImapString getKeyedStringOrEmpty(String key) {
|
||||
return getKeyedStringOrEmpty(key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link ImapString} keyed by {@code key}.
|
||||
* Return {@link ImapString#EMPTY} if not found.
|
||||
*/
|
||||
public final ImapString getKeyedStringOrEmpty(String key, boolean prefixMatch) {
|
||||
ImapElement e = getKeyedElementOrNull(key, prefixMatch);
|
||||
return (e != null) ? ((ImapString) e) : ImapString.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if it contains {@code s}.
|
||||
*/
|
||||
public final boolean contains(String s) {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (getStringOrEmpty(i).is(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (mList != null) {
|
||||
for (ImapElement e : mList) {
|
||||
e.destroy();
|
||||
}
|
||||
mList = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mList.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text representations of the contents concatenated with ",".
|
||||
*/
|
||||
public final String flatten() {
|
||||
return flatten(new StringBuilder()).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text representations (i.e. getString()) of contents joined together with
|
||||
* "," as the separator.
|
||||
*
|
||||
* Only used for building the capability string passed to vendor policies.
|
||||
*
|
||||
* We can't use toString(), because it's for debugging (meaning the format may change any time),
|
||||
* and it won't expand literals.
|
||||
*/
|
||||
private final StringBuilder flatten(StringBuilder sb) {
|
||||
sb.append('[');
|
||||
for (int i = 0; i < mList.size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
final ImapElement e = getElementOrNone(i);
|
||||
if (e.isList()) {
|
||||
getListOrEmpty(i).flatten(sb);
|
||||
} else if (e.isString()) {
|
||||
sb.append(getStringOrEmpty(i).getString());
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsForTest(ImapElement that) {
|
||||
if (!super.equalsForTest(that)) {
|
||||
return false;
|
||||
}
|
||||
ImapList thatList = (ImapList) that;
|
||||
if (size() != thatList.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (!mList.get(i).equalsForTest(thatList.getElementOrNone(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import com.android.email.FixedLengthInputStream;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Subclass of {@link ImapString} used for literals backed by an in-memory byte array.
|
||||
*/
|
||||
public class ImapMemoryLiteral extends ImapString {
|
||||
private byte[] mData;
|
||||
|
||||
/* package */ ImapMemoryLiteral(FixedLengthInputStream in) throws IOException {
|
||||
// We could use ByteArrayOutputStream and IOUtils.copy, but it'd perform an unnecessary
|
||||
// copy....
|
||||
mData = new byte[in.getLength()];
|
||||
int pos = 0;
|
||||
while (pos < mData.length) {
|
||||
int read = in.read(mData, pos, mData.length - pos);
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
pos += read;
|
||||
}
|
||||
if (pos != mData.length) {
|
||||
Log.w(Logging.LOG_TAG, "");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
mData = null;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return Utility.fromAscii(mData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getAsStream() {
|
||||
return new ByteArrayInputStream(mData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{%d byte literal(memory)}", mData.length);
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
|
||||
/**
|
||||
* Class represents an IMAP response.
|
||||
*/
|
||||
public class ImapResponse extends ImapList {
|
||||
private final String mTag;
|
||||
private final boolean mIsContinuationRequest;
|
||||
|
||||
/* package */ ImapResponse(String tag, boolean isContinuationRequest) {
|
||||
mTag = tag;
|
||||
mIsContinuationRequest = isContinuationRequest;
|
||||
}
|
||||
|
||||
/* package */ static boolean isStatusResponse(String symbol) {
|
||||
return ImapConstants.OK.equalsIgnoreCase(symbol)
|
||||
|| ImapConstants.NO.equalsIgnoreCase(symbol)
|
||||
|| ImapConstants.BAD.equalsIgnoreCase(symbol)
|
||||
|| ImapConstants.PREAUTH.equalsIgnoreCase(symbol)
|
||||
|| ImapConstants.BYE.equalsIgnoreCase(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it's a tagged response.
|
||||
*/
|
||||
public boolean isTagged() {
|
||||
return mTag != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it's a continuation request.
|
||||
*/
|
||||
public boolean isContinuationRequest() {
|
||||
return mIsContinuationRequest;
|
||||
}
|
||||
|
||||
public boolean isStatusResponse() {
|
||||
return isStatusResponse(getStringOrEmpty(0).getString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it's an OK response.
|
||||
*/
|
||||
public boolean isOk() {
|
||||
return is(0, ImapConstants.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it's an BAD response.
|
||||
*/
|
||||
public boolean isBad() {
|
||||
return is(0, ImapConstants.BAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it's an NO response.
|
||||
*/
|
||||
public boolean isNo() {
|
||||
return is(0, ImapConstants.NO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it's an {@code responseType} data response. (i.e. not tagged).
|
||||
* @param index where {@code responseType} should appear. e.g. 1 for "FETCH"
|
||||
* @param responseType e.g. "FETCH"
|
||||
*/
|
||||
public final boolean isDataResponse(int index, String responseType) {
|
||||
return !isTagged() && getStringOrEmpty(index).is(responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response code (RFC 3501 7.1) if it's a status response.
|
||||
*
|
||||
* e.g. "ALERT" for "* OK [ALERT] System shutdown in 10 minutes"
|
||||
*/
|
||||
public ImapString getResponseCodeOrEmpty() {
|
||||
if (!isStatusResponse()) {
|
||||
return ImapString.EMPTY; // Not a status response.
|
||||
}
|
||||
return getListOrEmpty(1).getStringOrEmpty(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Alert message it it has ALERT response code.
|
||||
*
|
||||
* e.g. "System shutdown in 10 minutes" for "* OK [ALERT] System shutdown in 10 minutes"
|
||||
*/
|
||||
public ImapString getAlertTextOrEmpty() {
|
||||
if (!getResponseCodeOrEmpty().is(ImapConstants.ALERT)) {
|
||||
return ImapString.EMPTY; // Not an ALERT
|
||||
}
|
||||
// The 3rd element contains all the rest of line.
|
||||
return getStringOrEmpty(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Response text in a status response.
|
||||
*/
|
||||
public ImapString getStatusResponseTextOrEmpty() {
|
||||
if (!isStatusResponse()) {
|
||||
return ImapString.EMPTY;
|
||||
}
|
||||
return getStringOrEmpty(getElementOrNone(1).isList() ? 2 : 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String tag = mTag;
|
||||
if (isContinuationRequest()) {
|
||||
tag = "+";
|
||||
}
|
||||
return "#" + tag + "# " + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsForTest(ImapElement that) {
|
||||
if (!super.equalsForTest(that)) {
|
||||
return false;
|
||||
}
|
||||
final ImapResponse thatResponse = (ImapResponse) that;
|
||||
if (mTag == null) {
|
||||
if (thatResponse.mTag != null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!mTag.equals(thatResponse.mTag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mIsContinuationRequest != thatResponse.mIsContinuationRequest) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,450 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.FixedLengthInputStream;
|
||||
import com.android.email.PeekableInputStream;
|
||||
import com.android.email.mail.transport.DiscourseLogger;
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.mail.MessagingException;
|
||||
import com.android.emailcommon.utility.LoggingInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* IMAP response parser.
|
||||
*/
|
||||
public class ImapResponseParser {
|
||||
private static final boolean DEBUG_LOG_RAW_STREAM = false; // DO NOT RELEASE AS 'TRUE'
|
||||
|
||||
/**
|
||||
* Literal larger than this will be stored in temp file.
|
||||
*/
|
||||
public static final int LITERAL_KEEP_IN_MEMORY_THRESHOLD = 2 * 1024 * 1024;
|
||||
|
||||
/** Input stream */
|
||||
private final PeekableInputStream mIn;
|
||||
|
||||
/**
|
||||
* To log network activities when the parser crashes.
|
||||
*
|
||||
* <p>We log all bytes received from the server, except for the part sent as literals.
|
||||
*/
|
||||
private final DiscourseLogger mDiscourseLogger;
|
||||
|
||||
private final int mLiteralKeepInMemoryThreshold;
|
||||
|
||||
/** StringBuilder used by readUntil() */
|
||||
private final StringBuilder mBufferReadUntil = new StringBuilder();
|
||||
|
||||
/** StringBuilder used by parseBareString() */
|
||||
private final StringBuilder mParseBareString = new StringBuilder();
|
||||
|
||||
/**
|
||||
* We store all {@link ImapResponse} in it. {@link #destroyResponses()} must be called from
|
||||
* time to time to destroy them and clear it.
|
||||
*/
|
||||
private final ArrayList<ImapResponse> mResponsesToDestroy = new ArrayList<ImapResponse>();
|
||||
|
||||
/**
|
||||
* Exception thrown when we receive BYE. It derives from IOException, so it'll be treated
|
||||
* in the same way EOF does.
|
||||
*/
|
||||
public static class ByeException extends IOException {
|
||||
public static final String MESSAGE = "Received BYE";
|
||||
public ByeException() {
|
||||
super(MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor for normal use.
|
||||
*/
|
||||
public ImapResponseParser(InputStream in, DiscourseLogger discourseLogger) {
|
||||
this(in, discourseLogger, LITERAL_KEEP_IN_MEMORY_THRESHOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for testing to override the literal size threshold.
|
||||
*/
|
||||
/* package for test */ ImapResponseParser(InputStream in, DiscourseLogger discourseLogger,
|
||||
int literalKeepInMemoryThreshold) {
|
||||
if (DEBUG_LOG_RAW_STREAM && MailActivityEmail.DEBUG) {
|
||||
in = new LoggingInputStream(in);
|
||||
}
|
||||
mIn = new PeekableInputStream(in);
|
||||
mDiscourseLogger = discourseLogger;
|
||||
mLiteralKeepInMemoryThreshold = literalKeepInMemoryThreshold;
|
||||
}
|
||||
|
||||
private static IOException newEOSException() {
|
||||
final String message = "End of stream reached";
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, message);
|
||||
}
|
||||
return new IOException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek next one byte.
|
||||
*
|
||||
* Throws IOException() if reaches EOF. As long as logical response lines end with \r\n,
|
||||
* we shouldn't see EOF during parsing.
|
||||
*/
|
||||
private int peek() throws IOException {
|
||||
final int next = mIn.peek();
|
||||
if (next == -1) {
|
||||
throw newEOSException();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return one byte from {@link #mIn}, and put it in {@link #mDiscourseLogger}.
|
||||
*
|
||||
* Throws IOException() if reaches EOF. As long as logical response lines end with \r\n,
|
||||
* we shouldn't see EOF during parsing.
|
||||
*/
|
||||
private int readByte() throws IOException {
|
||||
int next = mIn.read();
|
||||
if (next == -1) {
|
||||
throw newEOSException();
|
||||
}
|
||||
mDiscourseLogger.addReceivedByte(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy all the {@link ImapResponse}s stored in the internal storage and clear it.
|
||||
*
|
||||
* @see #readResponse()
|
||||
*/
|
||||
public void destroyResponses() {
|
||||
for (ImapResponse r : mResponsesToDestroy) {
|
||||
r.destroy();
|
||||
}
|
||||
mResponsesToDestroy.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next response available on the stream and returns an
|
||||
* {@link ImapResponse} object that represents it.
|
||||
*
|
||||
* <p>When this method successfully returns an {@link ImapResponse}, the {@link ImapResponse}
|
||||
* is stored in the internal storage. When the {@link ImapResponse} is no longer used
|
||||
* {@link #destroyResponses} should be called to destroy all the responses in the array.
|
||||
*
|
||||
* @return the parsed {@link ImapResponse} object.
|
||||
* @exception ByeException when detects BYE.
|
||||
*/
|
||||
public ImapResponse readResponse() throws IOException, MessagingException {
|
||||
ImapResponse response = null;
|
||||
try {
|
||||
response = parseResponse();
|
||||
if (MailActivityEmail.DEBUG) {
|
||||
Log.d(Logging.LOG_TAG, "<<< " + response.toString());
|
||||
}
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
// Parser crash -- log network activities.
|
||||
onParseError(e);
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
// Network error, or received an unexpected char.
|
||||
onParseError(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Handle this outside of try-catch. We don't have to dump protocol log when getting BYE.
|
||||
if (response.is(0, ImapConstants.BYE)) {
|
||||
Log.w(Logging.LOG_TAG, ByeException.MESSAGE);
|
||||
response.destroy();
|
||||
throw new ByeException();
|
||||
}
|
||||
mResponsesToDestroy.add(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private void onParseError(Exception e) {
|
||||
// Read a few more bytes, so that the log will contain some more context, even if the parser
|
||||
// crashes in the middle of a response.
|
||||
// This also makes sure the byte in question will be logged, no matter where it crashes.
|
||||
// e.g. when parseAtom() peeks and finds at an unexpected char, it throws an exception
|
||||
// before actually reading it.
|
||||
// However, we don't want to read too much, because then it may get into an email message.
|
||||
try {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int b = readByte();
|
||||
if (b == -1 || b == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
Log.w(Logging.LOG_TAG, "Exception detected: " + e.getMessage());
|
||||
mDiscourseLogger.logLastDiscourse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read next byte from stream and throw it away. If the byte is different from {@code expected}
|
||||
* throw {@link MessagingException}.
|
||||
*/
|
||||
/* package for test */ void expect(char expected) throws IOException {
|
||||
final int next = readByte();
|
||||
if (expected != next) {
|
||||
throw new IOException(String.format("Expected %04x (%c) but got %04x (%c)",
|
||||
(int) expected, expected, next, (char) next));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes until we find {@code end}, and return all as string.
|
||||
* The {@code end} will be read (rather than peeked) and won't be included in the result.
|
||||
*/
|
||||
/* package for test */ String readUntil(char end) throws IOException {
|
||||
mBufferReadUntil.setLength(0);
|
||||
for (;;) {
|
||||
final int ch = readByte();
|
||||
if (ch != end) {
|
||||
mBufferReadUntil.append((char) ch);
|
||||
} else {
|
||||
return mBufferReadUntil.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all bytes until \r\n.
|
||||
*/
|
||||
/* package */ String readUntilEol() throws IOException {
|
||||
String ret = readUntil('\r');
|
||||
expect('\n'); // TODO Should this really be error?
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and return the response line.
|
||||
*/
|
||||
private ImapResponse parseResponse() throws IOException, MessagingException {
|
||||
// We need to destroy the response if we get an exception.
|
||||
// So, we first store the response that's being built in responseToDestroy, until it's
|
||||
// completely built, at which point we copy it into responseToReturn and null out
|
||||
// responseToDestroyt.
|
||||
// If responseToDestroy is not null in finally, we destroy it because that means
|
||||
// we got an exception somewhere.
|
||||
ImapResponse responseToDestroy = null;
|
||||
final ImapResponse responseToReturn;
|
||||
|
||||
try {
|
||||
final int ch = peek();
|
||||
if (ch == '+') { // Continuation request
|
||||
readByte(); // skip +
|
||||
expect(' ');
|
||||
responseToDestroy = new ImapResponse(null, true);
|
||||
|
||||
// If it's continuation request, we don't really care what's in it.
|
||||
responseToDestroy.add(new ImapSimpleString(readUntilEol()));
|
||||
|
||||
// Response has successfully been built. Let's return it.
|
||||
responseToReturn = responseToDestroy;
|
||||
responseToDestroy = null;
|
||||
} else {
|
||||
// Status response or response data
|
||||
final String tag;
|
||||
if (ch == '*') {
|
||||
tag = null;
|
||||
readByte(); // skip *
|
||||
expect(' ');
|
||||
} else {
|
||||
tag = readUntil(' ');
|
||||
}
|
||||
responseToDestroy = new ImapResponse(tag, false);
|
||||
|
||||
final ImapString firstString = parseBareString();
|
||||
responseToDestroy.add(firstString);
|
||||
|
||||
// parseBareString won't eat a space after the string, so we need to skip it,
|
||||
// if exists.
|
||||
// If the next char is not ' ', it should be EOL.
|
||||
if (peek() == ' ') {
|
||||
readByte(); // skip ' '
|
||||
|
||||
if (responseToDestroy.isStatusResponse()) { // It's a status response
|
||||
|
||||
// Is there a response code?
|
||||
final int next = peek();
|
||||
if (next == '[') {
|
||||
responseToDestroy.add(parseList('[', ']'));
|
||||
if (peek() == ' ') { // Skip following space
|
||||
readByte();
|
||||
}
|
||||
}
|
||||
|
||||
String rest = readUntilEol();
|
||||
if (!TextUtils.isEmpty(rest)) {
|
||||
// The rest is free-form text.
|
||||
responseToDestroy.add(new ImapSimpleString(rest));
|
||||
}
|
||||
} else { // It's a response data.
|
||||
parseElements(responseToDestroy, '\0');
|
||||
}
|
||||
} else {
|
||||
expect('\r');
|
||||
expect('\n');
|
||||
}
|
||||
|
||||
// Response has successfully been built. Let's return it.
|
||||
responseToReturn = responseToDestroy;
|
||||
responseToDestroy = null;
|
||||
}
|
||||
} finally {
|
||||
if (responseToDestroy != null) {
|
||||
// We get an exception.
|
||||
responseToDestroy.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
return responseToReturn;
|
||||
}
|
||||
|
||||
private ImapElement parseElement() throws IOException, MessagingException {
|
||||
final int next = peek();
|
||||
switch (next) {
|
||||
case '(':
|
||||
return parseList('(', ')');
|
||||
case '[':
|
||||
return parseList('[', ']');
|
||||
case '"':
|
||||
readByte(); // Skip "
|
||||
return new ImapSimpleString(readUntil('"'));
|
||||
case '{':
|
||||
return parseLiteral();
|
||||
case '\r': // CR
|
||||
readByte(); // Consume \r
|
||||
expect('\n'); // Should be followed by LF.
|
||||
return null;
|
||||
case '\n': // LF // There shouldn't be a bare LF, but just in case.
|
||||
readByte(); // Consume \n
|
||||
return null;
|
||||
default:
|
||||
return parseBareString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an atom.
|
||||
*
|
||||
* Special case: If an atom contains '[', everything until the next ']' will be considered
|
||||
* a part of the atom.
|
||||
* (e.g. "BODY[HEADER.FIELDS ("DATE" ...)]" will become a single ImapString)
|
||||
*
|
||||
* If the value is "NIL", returns an empty string.
|
||||
*/
|
||||
private ImapString parseBareString() throws IOException, MessagingException {
|
||||
mParseBareString.setLength(0);
|
||||
for (;;) {
|
||||
final int ch = peek();
|
||||
|
||||
// TODO Can we clean this up? (This condition is from the old parser.)
|
||||
if (ch == '(' || ch == ')' || ch == '{' || ch == ' ' ||
|
||||
// ']' is not part of atom (it's in resp-specials)
|
||||
ch == ']' ||
|
||||
// docs claim that flags are \ atom but atom isn't supposed to
|
||||
// contain
|
||||
// * and some flags contain *
|
||||
// ch == '%' || ch == '*' ||
|
||||
ch == '%' ||
|
||||
// TODO probably should not allow \ and should recognize
|
||||
// it as a flag instead
|
||||
// ch == '"' || ch == '\' ||
|
||||
ch == '"' || (0x00 <= ch && ch <= 0x1f) || ch == 0x7f) {
|
||||
if (mParseBareString.length() == 0) {
|
||||
throw new MessagingException("Expected string, none found.");
|
||||
}
|
||||
String s = mParseBareString.toString();
|
||||
|
||||
// NIL will be always converted into the empty string.
|
||||
if (ImapConstants.NIL.equalsIgnoreCase(s)) {
|
||||
return ImapString.EMPTY;
|
||||
}
|
||||
return new ImapSimpleString(s);
|
||||
} else if (ch == '[') {
|
||||
// Eat all until next ']'
|
||||
mParseBareString.append((char) readByte());
|
||||
mParseBareString.append(readUntil(']'));
|
||||
mParseBareString.append(']'); // readUntil won't include the end char.
|
||||
} else {
|
||||
mParseBareString.append((char) readByte());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseElements(ImapList list, char end)
|
||||
throws IOException, MessagingException {
|
||||
for (;;) {
|
||||
for (;;) {
|
||||
final int next = peek();
|
||||
if (next == end) {
|
||||
return;
|
||||
}
|
||||
if (next != ' ') {
|
||||
break;
|
||||
}
|
||||
// Skip space
|
||||
readByte();
|
||||
}
|
||||
final ImapElement el = parseElement();
|
||||
if (el == null) { // EOL
|
||||
return;
|
||||
}
|
||||
list.add(el);
|
||||
}
|
||||
}
|
||||
|
||||
private ImapList parseList(char opening, char closing)
|
||||
throws IOException, MessagingException {
|
||||
expect(opening);
|
||||
final ImapList list = new ImapList();
|
||||
parseElements(list, closing);
|
||||
expect(closing);
|
||||
return list;
|
||||
}
|
||||
|
||||
private ImapString parseLiteral() throws IOException, MessagingException {
|
||||
expect('{');
|
||||
final int size;
|
||||
try {
|
||||
size = Integer.parseInt(readUntil('}'));
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new MessagingException("Invalid length in literal");
|
||||
}
|
||||
expect('\r');
|
||||
expect('\n');
|
||||
FixedLengthInputStream in = new FixedLengthInputStream(mIn, size);
|
||||
if (size > mLiteralKeepInMemoryThreshold) {
|
||||
return new ImapTempFileLiteral(in);
|
||||
} else {
|
||||
return new ImapMemoryLiteral(in);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Subclass of {@link ImapString} used for non literals.
|
||||
*/
|
||||
public class ImapSimpleString extends ImapString {
|
||||
private String mString;
|
||||
|
||||
/* package */ ImapSimpleString(String string) {
|
||||
mString = (string != null) ? string : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
mString = null;
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return mString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getAsStream() {
|
||||
return new ByteArrayInputStream(Utility.toAscii(mString));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Purposefully not return just mString, in order to prevent using it instead of getString.
|
||||
return "\"" + mString + "\"";
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import com.android.emailcommon.Logging;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Class represents an IMAP "element" that is not a list.
|
||||
*
|
||||
* An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too.
|
||||
* Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]".
|
||||
* See {@link ImapResponseParser}.
|
||||
*/
|
||||
public abstract class ImapString extends ImapElement {
|
||||
private static final byte[] EMPTY_BYTES = new byte[0];
|
||||
|
||||
public static final ImapString EMPTY = new ImapString() {
|
||||
@Override public void destroy() {
|
||||
// Don't call super.destroy().
|
||||
// It's a shared object. We don't want the mDestroyed to be set on this.
|
||||
}
|
||||
|
||||
@Override public String getString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override public InputStream getAsStream() {
|
||||
return new ByteArrayInputStream(EMPTY_BYTES);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// This is used only for parsing IMAP's FETCH ENVELOPE command, in which
|
||||
// en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be
|
||||
// handled by Locale.US
|
||||
private final static SimpleDateFormat DATE_TIME_FORMAT =
|
||||
new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
|
||||
|
||||
private boolean mIsInteger;
|
||||
private int mParsedInteger;
|
||||
private Date mParsedDate;
|
||||
|
||||
@Override
|
||||
public final boolean isList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isString() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if and only if the length of the string is larger than 0.
|
||||
*
|
||||
* Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser
|
||||
* #parseBareString}.
|
||||
* On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is
|
||||
* treated literally.
|
||||
*/
|
||||
public final boolean isEmpty() {
|
||||
return getString().length() == 0;
|
||||
}
|
||||
|
||||
public abstract String getString();
|
||||
|
||||
public abstract InputStream getAsStream();
|
||||
|
||||
/**
|
||||
* @return whether it can be parsed as a number.
|
||||
*/
|
||||
public final boolean isNumber() {
|
||||
if (mIsInteger) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
mParsedInteger = Integer.parseInt(getString());
|
||||
mIsInteger = true;
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return value parsed as a number.
|
||||
*/
|
||||
public final int getNumberOrZero() {
|
||||
if (!isNumber()) {
|
||||
return 0;
|
||||
}
|
||||
return mParsedInteger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}.
|
||||
*/
|
||||
public final boolean isDate() {
|
||||
if (mParsedDate != null) {
|
||||
return true;
|
||||
}
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
mParsedDate = DATE_TIME_FORMAT.parse(getString());
|
||||
return true;
|
||||
} catch (ParseException e) {
|
||||
Log.w(Logging.LOG_TAG, getString() + " can't be parsed as a date.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return value it can be parsed as a {@link Date}, or null otherwise.
|
||||
*/
|
||||
public final Date getDateOrNull() {
|
||||
if (!isDate()) {
|
||||
return null;
|
||||
}
|
||||
return mParsedDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the value case-insensitively equals to {@code s}.
|
||||
*/
|
||||
public final boolean is(String s) {
|
||||
if (s == null) {
|
||||
return false;
|
||||
}
|
||||
return getString().equalsIgnoreCase(s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return whether the value case-insensitively starts with {@code s}.
|
||||
*/
|
||||
public final boolean startsWith(String prefix) {
|
||||
if (prefix == null) {
|
||||
return false;
|
||||
}
|
||||
final String me = this.getString();
|
||||
if (me.length() < prefix.length()) {
|
||||
return false;
|
||||
}
|
||||
return me.substring(0, prefix.length()).equalsIgnoreCase(prefix);
|
||||
}
|
||||
|
||||
// To force subclasses to implement it.
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
@Override
|
||||
public final boolean equalsForTest(ImapElement that) {
|
||||
if (!super.equalsForTest(that)) {
|
||||
return false;
|
||||
}
|
||||
ImapString thatString = (ImapString) that;
|
||||
return getString().equals(thatString.getString());
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.FixedLengthInputStream;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.TempDirectory;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Subclass of {@link ImapString} used for literals backed by a temp file.
|
||||
*/
|
||||
public class ImapTempFileLiteral extends ImapString {
|
||||
/* package for test */ final File mFile;
|
||||
|
||||
/** Size is purely for toString() */
|
||||
private final int mSize;
|
||||
|
||||
/* package */ ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
|
||||
mSize = stream.getLength();
|
||||
mFile = File.createTempFile("imap", ".tmp", TempDirectory.getTempDirectory());
|
||||
|
||||
// Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
|
||||
// so it'd simply cause a memory leak.
|
||||
// deleteOnExit() simply adds filenames to a static list and the list will never shrink.
|
||||
// mFile.deleteOnExit();
|
||||
OutputStream out = new FileOutputStream(mFile);
|
||||
IOUtils.copy(stream, out);
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we delete the temp file.
|
||||
*
|
||||
* We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
destroy();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getAsStream() {
|
||||
checkNotDestroyed();
|
||||
try {
|
||||
return new FileInputStream(mFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
// It's probably possible if we're low on storage and the system clears the cache dir.
|
||||
Log.w(Logging.LOG_TAG, "ImapTempFileLiteral: Temp file not found");
|
||||
|
||||
// Return 0 byte stream as a dummy...
|
||||
return new ByteArrayInputStream(new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
checkNotDestroyed();
|
||||
try {
|
||||
byte[] bytes = IOUtils.toByteArray(getAsStream());
|
||||
// Prevent crash from OOM; we've seen this, but only rarely and not reproducibly
|
||||
if (bytes.length > ImapResponseParser.LITERAL_KEEP_IN_MEMORY_THRESHOLD) {
|
||||
throw new IOException();
|
||||
}
|
||||
return Utility.fromAscii(bytes);
|
||||
} catch (IOException e) {
|
||||
Log.w(Logging.LOG_TAG, "ImapTempFileLiteral: Error while reading temp file", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
if (!isDestroyed() && mFile.exists()) {
|
||||
mFile.delete();
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
// Just log and ignore.
|
||||
Log.w(Logging.LOG_TAG, "Failed to remove temp file: " + re.getMessage());
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{%d byte literal(file)}", mSize);
|
||||
}
|
||||
|
||||
public boolean tempFileExistsForTest() {
|
||||
return mFile.exists();
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.mail.store.imap;
|
||||
|
||||
import com.android.emailcommon.Logging;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility methods for use with IMAP.
|
||||
*/
|
||||
public class ImapUtility {
|
||||
/**
|
||||
* Apply quoting rules per IMAP RFC,
|
||||
* quoted = DQUOTE *QUOTED-CHAR DQUOTE
|
||||
* QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
|
||||
* quoted-specials = DQUOTE / "\"
|
||||
*
|
||||
* This is used primarily for IMAP login, but might be useful elsewhere.
|
||||
*
|
||||
* NOTE: Not very efficient - you may wish to preflight this, or perhaps it should check
|
||||
* for trouble chars before calling the replace functions.
|
||||
*
|
||||
* @param s The string to be quoted.
|
||||
* @return A copy of the string, having undergone quoting as described above
|
||||
*/
|
||||
public static String imapQuoted(String s) {
|
||||
|
||||
// First, quote any backslashes by replacing \ with \\
|
||||
// regex Pattern: \\ (Java string const = \\\\)
|
||||
// Substitute: \\\\ (Java string const = \\\\\\\\)
|
||||
String result = s.replaceAll("\\\\", "\\\\\\\\");
|
||||
|
||||
// Then, quote any double-quotes by replacing " with \"
|
||||
// regex Pattern: " (Java string const = \")
|
||||
// Substitute: \\" (Java string const = \\\\\")
|
||||
result = result.replaceAll("\"", "\\\\\"");
|
||||
|
||||
// return string with quotes around it
|
||||
return "\"" + result + "\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the values in a sequence set per RFC 3501. Any ranges are expanded into a
|
||||
* list of individual numbers. If the set is invalid, an empty array is returned.
|
||||
* <pre>
|
||||
* sequence-number = nz-number / "*"
|
||||
* sequence-range = sequence-number ":" sequence-number
|
||||
* sequence-set = (sequence-number / sequence-range) *("," sequence-set)
|
||||
* </pre>
|
||||
*/
|
||||
public static String[] getImapSequenceValues(String set) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
if (set != null) {
|
||||
String[] setItems = set.split(",");
|
||||
for (String item : setItems) {
|
||||
if (item.indexOf(':') == -1) {
|
||||
// simple item
|
||||
try {
|
||||
Integer.parseInt(item); // Don't need the value; just ensure it's valid
|
||||
list.add(item);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.d(Logging.LOG_TAG, "Invalid UID value", e);
|
||||
}
|
||||
} else {
|
||||
// range
|
||||
for (String rangeItem : getImapRangeValues(item)) {
|
||||
list.add(rangeItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] stringList = new String[list.size()];
|
||||
return list.toArray(stringList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the given number range into a list of individual numbers. If the range is not valid,
|
||||
* an empty array is returned.
|
||||
* <pre>
|
||||
* sequence-number = nz-number / "*"
|
||||
* sequence-range = sequence-number ":" sequence-number
|
||||
* sequence-set = (sequence-number / sequence-range) *("," sequence-set)
|
||||
* </pre>
|
||||
*/
|
||||
public static String[] getImapRangeValues(String range) {
|
||||
ArrayList<String> list = new ArrayList<String>();
|
||||
try {
|
||||
if (range != null) {
|
||||
int colonPos = range.indexOf(':');
|
||||
if (colonPos > 0) {
|
||||
int first = Integer.parseInt(range.substring(0, colonPos));
|
||||
int second = Integer.parseInt(range.substring(colonPos + 1));
|
||||
if (first < second) {
|
||||
for (int i = first; i <= second; i++) {
|
||||
list.add(Integer.toString(i));
|
||||
}
|
||||
} else {
|
||||
for (int i = first; i >= second; i--) {
|
||||
list.add(Integer.toString(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Log.d(Logging.LOG_TAG, "Invalid range value", e);
|
||||
}
|
||||
String[] stringList = new String[list.size()];
|
||||
return list.toArray(stringList);
|
||||
}
|
||||
}
|
|
@ -49,10 +49,10 @@ import java.util.List;
|
|||
* A simple ContentProvider that allows file access to Email's attachments.
|
||||
*
|
||||
* The URI scheme is as follows. For raw file access:
|
||||
* content://com.android.email.attachmentprovider/acct#/attach#/RAW
|
||||
* content://com.android.mail.attachmentprovider/acct#/attach#/RAW
|
||||
*
|
||||
* And for access to thumbnails:
|
||||
* content://com.android.email.attachmentprovider/acct#/attach#/THUMBNAIL/width#/height#
|
||||
* content://com.android.mail.attachmentprovider/acct#/attach#/THUMBNAIL/width#/height#
|
||||
*
|
||||
* The on-disk (storage) schema is as follows.
|
||||
*
|
||||
|
|
|
@ -29,7 +29,6 @@ import android.provider.ContactsContract;
|
|||
import android.util.Log;
|
||||
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
import com.android.emailcommon.AccountManagerTypes;
|
||||
import com.android.emailcommon.mail.Address;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
|
@ -586,7 +585,7 @@ public final class DBHelper {
|
|||
// Versions >= 5 require that data be preserved!
|
||||
if (oldVersion < 5) {
|
||||
android.accounts.Account[] accounts = AccountManager.get(mContext)
|
||||
.getAccountsByType(AccountManagerTypes.TYPE_EXCHANGE);
|
||||
.getAccountsByType("eas");
|
||||
for (android.accounts.Account account: accounts) {
|
||||
AccountManager.get(mContext).removeAccount(account, null, null);
|
||||
}
|
||||
|
@ -1092,8 +1091,9 @@ public final class DBHelper {
|
|||
static private void createAccountManagerAccount(Context context, String login,
|
||||
String password) {
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
// STOPSHIP
|
||||
android.accounts.Account amAccount =
|
||||
new android.accounts.Account(login, AccountManagerTypes.TYPE_POP_IMAP);
|
||||
new android.accounts.Account(login, "com.android.email");
|
||||
accountManager.addAccountExplicitly(amAccount, password, null);
|
||||
ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(amAccount, EmailContent.AUTHORITY, true);
|
||||
|
@ -1135,7 +1135,7 @@ public final class DBHelper {
|
|||
android.accounts.Account amAccount =
|
||||
new android.accounts.Account(
|
||||
accountCursor.getString(V21_ACCOUNT_EMAIL),
|
||||
AccountManagerTypes.TYPE_EXCHANGE);
|
||||
"eas");
|
||||
ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(amAccount,
|
||||
EmailContent.AUTHORITY, true);
|
||||
|
|
|
@ -136,15 +136,6 @@ public class EmailProvider extends ContentProvider {
|
|||
public static final String EMAIL_ATTACHMENT_MIME_TYPE =
|
||||
"vnd.android.cursor.item/email-attachment";
|
||||
|
||||
public static final Uri INTEGRITY_CHECK_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/integrityCheck");
|
||||
public static final Uri ACCOUNT_BACKUP_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/accountBackup");
|
||||
public static final Uri FOLDER_STATUS_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/status");
|
||||
public static final Uri FOLDER_REFRESH_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/refresh");
|
||||
|
||||
/** Appended to the notification URI for delete operations */
|
||||
public static final String NOTIFICATION_OP_DELETE = "delete";
|
||||
/** Appended to the notification URI for insert operations */
|
||||
|
@ -310,7 +301,7 @@ public class EmailProvider extends ContentProvider {
|
|||
null // UI
|
||||
};
|
||||
|
||||
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
private static UriMatcher sURIMatcher = null;
|
||||
|
||||
private static final String MAILBOX_PRE_CACHE_SELECTION = MailboxColumns.TYPE + " IN (" +
|
||||
Mailbox.TYPE_INBOX + "," + Mailbox.TYPE_DRAFTS + "," + Mailbox.TYPE_TRASH + "," +
|
||||
|
@ -341,7 +332,7 @@ public class EmailProvider extends ContentProvider {
|
|||
|
||||
private static final String ID_EQUALS = EmailContent.RECORD_ID + "=?";
|
||||
|
||||
private static final ContentValues CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT;
|
||||
private static ContentValues CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT;
|
||||
private static final ContentValues EMPTY_CONTENT_VALUES = new ContentValues();
|
||||
|
||||
public static final String MESSAGE_URI_PARAMETER_MAILBOX_ID = "mailboxId";
|
||||
|
@ -357,130 +348,6 @@ public class EmailProvider extends ContentProvider {
|
|||
private static final String SWIPE_DELETE = Integer.toString(Swipe.DELETE);
|
||||
private static final String SWIPE_DISABLED = Integer.toString(Swipe.DISABLED);
|
||||
|
||||
static {
|
||||
// Email URI matching table
|
||||
UriMatcher matcher = sURIMatcher;
|
||||
|
||||
// All accounts
|
||||
matcher.addURI(EmailContent.AUTHORITY, "account", ACCOUNT);
|
||||
// A specific account
|
||||
// insert into this URI causes a mailbox to be added to the account
|
||||
matcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "account/default", ACCOUNT_DEFAULT_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK);
|
||||
|
||||
// Special URI to reset the new message count. Only update works, and content values
|
||||
// will be ignored.
|
||||
matcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount",
|
||||
ACCOUNT_RESET_NEW_COUNT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount/#",
|
||||
ACCOUNT_RESET_NEW_COUNT_ID);
|
||||
|
||||
// All mailboxes
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX);
|
||||
// A specific mailbox
|
||||
// insert into this URI causes a message to be added to the mailbox
|
||||
// ** NOTE For now, the accountKey must be set manually in the values!
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailbox/#", MAILBOX_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxIdFromAccountAndType/#/#",
|
||||
MAILBOX_ID_FROM_ACCOUNT_AND_TYPE);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxNotification/#", MAILBOX_NOTIFICATION);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxMostRecentMessage/#",
|
||||
MAILBOX_MOST_RECENT_MESSAGE);
|
||||
|
||||
// All messages
|
||||
matcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE);
|
||||
// A specific message
|
||||
// insert into this URI causes an attachment to be added to the message
|
||||
matcher.addURI(EmailContent.AUTHORITY, "message/#", MESSAGE_ID);
|
||||
|
||||
// A specific attachment
|
||||
matcher.addURI(EmailContent.AUTHORITY, "attachment", ATTACHMENT);
|
||||
// A specific attachment (the header information)
|
||||
matcher.addURI(EmailContent.AUTHORITY, "attachment/#", ATTACHMENT_ID);
|
||||
// The attachments of a specific message (query only) (insert & delete TBD)
|
||||
matcher.addURI(EmailContent.AUTHORITY, "attachment/message/#",
|
||||
ATTACHMENTS_MESSAGE_ID);
|
||||
|
||||
// All mail bodies
|
||||
matcher.addURI(EmailContent.AUTHORITY, "body", BODY);
|
||||
// A specific mail body
|
||||
matcher.addURI(EmailContent.AUTHORITY, "body/#", BODY_ID);
|
||||
|
||||
// All hostauth records
|
||||
matcher.addURI(EmailContent.AUTHORITY, "hostauth", HOSTAUTH);
|
||||
// A specific hostauth
|
||||
matcher.addURI(EmailContent.AUTHORITY, "hostauth/*", HOSTAUTH_ID);
|
||||
|
||||
// Atomically a constant value to a particular field of a mailbox/account
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxIdAddToField/#",
|
||||
MAILBOX_ID_ADD_TO_FIELD);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "accountIdAddToField/#",
|
||||
ACCOUNT_ID_ADD_TO_FIELD);
|
||||
|
||||
/**
|
||||
* THIS URI HAS SPECIAL SEMANTICS
|
||||
* ITS USE IS INTENDED FOR THE UI APPLICATION TO MARK CHANGES THAT NEED TO BE SYNCED BACK
|
||||
* TO A SERVER VIA A SYNC ADAPTER
|
||||
*/
|
||||
matcher.addURI(EmailContent.AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "messageBySelection", MESSAGE_SELECTION);
|
||||
|
||||
/**
|
||||
* THE URIs BELOW THIS POINT ARE INTENDED TO BE USED BY SYNC ADAPTERS ONLY
|
||||
* THEY REFER TO DATA CREATED AND MAINTAINED BY CALLS TO THE SYNCED_MESSAGE_ID URI
|
||||
* BY THE UI APPLICATION
|
||||
*/
|
||||
// All deleted messages
|
||||
matcher.addURI(EmailContent.AUTHORITY, "deletedMessage", DELETED_MESSAGE);
|
||||
// A specific deleted message
|
||||
matcher.addURI(EmailContent.AUTHORITY, "deletedMessage/#", DELETED_MESSAGE_ID);
|
||||
|
||||
// All updated messages
|
||||
matcher.addURI(EmailContent.AUTHORITY, "updatedMessage", UPDATED_MESSAGE);
|
||||
// A specific updated message
|
||||
matcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID);
|
||||
|
||||
CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT = new ContentValues();
|
||||
CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT.put(Account.NEW_MESSAGE_COUNT, 0);
|
||||
|
||||
matcher.addURI(EmailContent.AUTHORITY, "policy", POLICY);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID);
|
||||
|
||||
// All quick responses
|
||||
matcher.addURI(EmailContent.AUTHORITY, "quickresponse", QUICK_RESPONSE);
|
||||
// A specific quick response
|
||||
matcher.addURI(EmailContent.AUTHORITY, "quickresponse/#", QUICK_RESPONSE_ID);
|
||||
// All quick responses associated with a particular account id
|
||||
matcher.addURI(EmailContent.AUTHORITY, "quickresponse/account/#",
|
||||
QUICK_RESPONSE_ACCOUNT_ID);
|
||||
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uifolders/#", UI_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiallfolders/#", UI_ALL_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisubfolders/#", UI_SUBFOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uimessages/#", UI_MESSAGES);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uimessage/#", UI_MESSAGE);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisendmail/#", UI_SENDMAIL);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiundo", UI_UNDO);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisavedraft/#", UI_SAVEDRAFT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiupdatedraft/#", UI_UPDATEDRAFT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisenddraft/#", UI_SENDDRAFT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uirefresh/#", UI_FOLDER_REFRESH);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uifolder/#", UI_FOLDER);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccount/#", UI_ACCOUNT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccts", UI_ACCTS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiloadmore/#", UI_FOLDER_LOAD_MORE);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiconversation/#", UI_CONVERSATION);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uirecentfolders/#", UI_RECENT_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uidefaultrecentfolders/#",
|
||||
UI_DEFAULT_RECENT_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "pickTrashFolder/#", ACCOUNT_PICK_TRASH_FOLDER);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "pickSentFolder/#", ACCOUNT_PICK_SENT_FOLDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the UriMatcher call so we can throw a runtime exception if an unknown Uri is passed in
|
||||
|
@ -497,6 +364,11 @@ public class EmailProvider extends ContentProvider {
|
|||
return match;
|
||||
}
|
||||
|
||||
public static Uri INTEGRITY_CHECK_URI;
|
||||
public static Uri ACCOUNT_BACKUP_URI;
|
||||
public static Uri FOLDER_STATUS_URI;
|
||||
public static Uri FOLDER_REFRESH_URI;
|
||||
|
||||
private SQLiteDatabase mDatabase;
|
||||
private SQLiteDatabase mBodyDatabase;
|
||||
|
||||
|
@ -1171,10 +1043,147 @@ public class EmailProvider extends ContentProvider {
|
|||
return resultUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
MailActivityEmail.setServicesEnabledAsync(getContext());
|
||||
checkDatabases();
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
Context context = getContext();
|
||||
EmailContent.init(context);
|
||||
if (INTEGRITY_CHECK_URI == null) {
|
||||
INTEGRITY_CHECK_URI = Uri.parse("content://" + EmailContent.AUTHORITY +
|
||||
"/integrityCheck");
|
||||
ACCOUNT_BACKUP_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/accountBackup");
|
||||
FOLDER_STATUS_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/status");
|
||||
FOLDER_REFRESH_URI =
|
||||
Uri.parse("content://" + EmailContent.AUTHORITY + "/refresh");
|
||||
}
|
||||
MailActivityEmail.setServicesEnabledAsync(context);
|
||||
checkDatabases();
|
||||
if (sURIMatcher == null) {
|
||||
sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
// Email URI matching table
|
||||
UriMatcher matcher = sURIMatcher;
|
||||
|
||||
// All accounts
|
||||
matcher.addURI(EmailContent.AUTHORITY, "account", ACCOUNT);
|
||||
// A specific account
|
||||
// insert into this URI causes a mailbox to be added to the account
|
||||
matcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "account/default", ACCOUNT_DEFAULT_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK);
|
||||
|
||||
// Special URI to reset the new message count. Only update works, and content values
|
||||
// will be ignored.
|
||||
matcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount",
|
||||
ACCOUNT_RESET_NEW_COUNT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "resetNewMessageCount/#",
|
||||
ACCOUNT_RESET_NEW_COUNT_ID);
|
||||
|
||||
// All mailboxes
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX);
|
||||
// A specific mailbox
|
||||
// insert into this URI causes a message to be added to the mailbox
|
||||
// ** NOTE For now, the accountKey must be set manually in the values!
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailbox/#", MAILBOX_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxIdFromAccountAndType/#/#",
|
||||
MAILBOX_ID_FROM_ACCOUNT_AND_TYPE);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxNotification/#", MAILBOX_NOTIFICATION);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxMostRecentMessage/#",
|
||||
MAILBOX_MOST_RECENT_MESSAGE);
|
||||
|
||||
// All messages
|
||||
matcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE);
|
||||
// A specific message
|
||||
// insert into this URI causes an attachment to be added to the message
|
||||
matcher.addURI(EmailContent.AUTHORITY, "message/#", MESSAGE_ID);
|
||||
|
||||
// A specific attachment
|
||||
matcher.addURI(EmailContent.AUTHORITY, "attachment", ATTACHMENT);
|
||||
// A specific attachment (the header information)
|
||||
matcher.addURI(EmailContent.AUTHORITY, "attachment/#", ATTACHMENT_ID);
|
||||
// The attachments of a specific message (query only) (insert & delete TBD)
|
||||
matcher.addURI(EmailContent.AUTHORITY, "attachment/message/#",
|
||||
ATTACHMENTS_MESSAGE_ID);
|
||||
|
||||
// All mail bodies
|
||||
matcher.addURI(EmailContent.AUTHORITY, "body", BODY);
|
||||
// A specific mail body
|
||||
matcher.addURI(EmailContent.AUTHORITY, "body/#", BODY_ID);
|
||||
|
||||
// All hostauth records
|
||||
matcher.addURI(EmailContent.AUTHORITY, "hostauth", HOSTAUTH);
|
||||
// A specific hostauth
|
||||
matcher.addURI(EmailContent.AUTHORITY, "hostauth/*", HOSTAUTH_ID);
|
||||
|
||||
// Atomically a constant value to a particular field of a mailbox/account
|
||||
matcher.addURI(EmailContent.AUTHORITY, "mailboxIdAddToField/#",
|
||||
MAILBOX_ID_ADD_TO_FIELD);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "accountIdAddToField/#",
|
||||
ACCOUNT_ID_ADD_TO_FIELD);
|
||||
|
||||
/**
|
||||
* THIS URI HAS SPECIAL SEMANTICS
|
||||
* ITS USE IS INTENDED FOR THE UI TO MARK CHANGES THAT NEED TO BE SYNCED BACK
|
||||
* TO A SERVER VIA A SYNC ADAPTER
|
||||
*/
|
||||
matcher.addURI(EmailContent.AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "messageBySelection", MESSAGE_SELECTION);
|
||||
|
||||
/**
|
||||
* THE URIs BELOW THIS POINT ARE INTENDED TO BE USED BY SYNC ADAPTERS ONLY
|
||||
* THEY REFER TO DATA CREATED AND MAINTAINED BY CALLS TO THE SYNCED_MESSAGE_ID URI
|
||||
* BY THE UI APPLICATION
|
||||
*/
|
||||
// All deleted messages
|
||||
matcher.addURI(EmailContent.AUTHORITY, "deletedMessage", DELETED_MESSAGE);
|
||||
// A specific deleted message
|
||||
matcher.addURI(EmailContent.AUTHORITY, "deletedMessage/#", DELETED_MESSAGE_ID);
|
||||
|
||||
// All updated messages
|
||||
matcher.addURI(EmailContent.AUTHORITY, "updatedMessage", UPDATED_MESSAGE);
|
||||
// A specific updated message
|
||||
matcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID);
|
||||
|
||||
CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT = new ContentValues();
|
||||
CONTENT_VALUES_RESET_NEW_MESSAGE_COUNT.put(Account.NEW_MESSAGE_COUNT, 0);
|
||||
|
||||
matcher.addURI(EmailContent.AUTHORITY, "policy", POLICY);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID);
|
||||
|
||||
// All quick responses
|
||||
matcher.addURI(EmailContent.AUTHORITY, "quickresponse", QUICK_RESPONSE);
|
||||
// A specific quick response
|
||||
matcher.addURI(EmailContent.AUTHORITY, "quickresponse/#", QUICK_RESPONSE_ID);
|
||||
// All quick responses associated with a particular account id
|
||||
matcher.addURI(EmailContent.AUTHORITY, "quickresponse/account/#",
|
||||
QUICK_RESPONSE_ACCOUNT_ID);
|
||||
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uifolders/#", UI_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiallfolders/#", UI_ALL_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisubfolders/#", UI_SUBFOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uimessages/#", UI_MESSAGES);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uimessage/#", UI_MESSAGE);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisendmail/#", UI_SENDMAIL);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiundo", UI_UNDO);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisavedraft/#", UI_SAVEDRAFT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiupdatedraft/#", UI_UPDATEDRAFT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisenddraft/#", UI_SENDDRAFT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uirefresh/#", UI_FOLDER_REFRESH);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uifolder/#", UI_FOLDER);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccount/#", UI_ACCOUNT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccts", UI_ACCTS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiloadmore/#", UI_FOLDER_LOAD_MORE);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uiconversation/#", UI_CONVERSATION);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uirecentfolders/#", UI_RECENT_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "uidefaultrecentfolders/#",
|
||||
UI_DEFAULT_RECENT_FOLDERS);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "pickTrashFolder/#", ACCOUNT_PICK_TRASH_FOLDER);
|
||||
matcher.addURI(EmailContent.AUTHORITY, "pickSentFolder/#", ACCOUNT_PICK_SENT_FOLDER);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2121,28 +2130,34 @@ outer:
|
|||
* Mapping of UIProvider columns to EmailProvider columns for the message list (called the
|
||||
* conversation list in UnifiedEmail)
|
||||
*/
|
||||
private static final ProjectionMap sMessageListMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, MessageColumns.ID)
|
||||
.add(UIProvider.ConversationColumns.URI, uriWithId("uimessage"))
|
||||
.add(UIProvider.ConversationColumns.MESSAGE_LIST_URI, uriWithId("uimessage"))
|
||||
.add(UIProvider.ConversationColumns.SUBJECT, MessageColumns.SUBJECT)
|
||||
.add(UIProvider.ConversationColumns.SNIPPET, MessageColumns.SNIPPET)
|
||||
.add(UIProvider.ConversationColumns.CONVERSATION_INFO, null)
|
||||
.add(UIProvider.ConversationColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
|
||||
.add(UIProvider.ConversationColumns.HAS_ATTACHMENTS, MessageColumns.FLAG_ATTACHMENT)
|
||||
.add(UIProvider.ConversationColumns.NUM_MESSAGES, "1")
|
||||
.add(UIProvider.ConversationColumns.NUM_DRAFTS, "0")
|
||||
.add(UIProvider.ConversationColumns.SENDING_STATE,
|
||||
Integer.toString(ConversationSendingState.OTHER))
|
||||
.add(UIProvider.ConversationColumns.PRIORITY, Integer.toString(ConversationPriority.LOW))
|
||||
.add(UIProvider.ConversationColumns.READ, MessageColumns.FLAG_READ)
|
||||
.add(UIProvider.ConversationColumns.STARRED, MessageColumns.FLAG_FAVORITE)
|
||||
.add(UIProvider.ConversationColumns.FLAGS, CONVERSATION_FLAGS)
|
||||
.add(UIProvider.ConversationColumns.ACCOUNT_URI,
|
||||
"'content://" + EmailContent.AUTHORITY + "/uiaccount/' || "
|
||||
+ MessageColumns.ACCOUNT_KEY)
|
||||
.add(UIProvider.ConversationColumns.SENDER_INFO, MessageColumns.FROM_LIST)
|
||||
.build();
|
||||
private ProjectionMap getMessageListMap() {
|
||||
if (sMessageListMap == null) {
|
||||
sMessageListMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, MessageColumns.ID)
|
||||
.add(UIProvider.ConversationColumns.URI, uriWithId("uimessage"))
|
||||
.add(UIProvider.ConversationColumns.MESSAGE_LIST_URI, uriWithId("uimessage"))
|
||||
.add(UIProvider.ConversationColumns.SUBJECT, MessageColumns.SUBJECT)
|
||||
.add(UIProvider.ConversationColumns.SNIPPET, MessageColumns.SNIPPET)
|
||||
.add(UIProvider.ConversationColumns.CONVERSATION_INFO, null)
|
||||
.add(UIProvider.ConversationColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
|
||||
.add(UIProvider.ConversationColumns.HAS_ATTACHMENTS, MessageColumns.FLAG_ATTACHMENT)
|
||||
.add(UIProvider.ConversationColumns.NUM_MESSAGES, "1")
|
||||
.add(UIProvider.ConversationColumns.NUM_DRAFTS, "0")
|
||||
.add(UIProvider.ConversationColumns.SENDING_STATE,
|
||||
Integer.toString(ConversationSendingState.OTHER))
|
||||
.add(UIProvider.ConversationColumns.PRIORITY,
|
||||
Integer.toString(ConversationPriority.LOW))
|
||||
.add(UIProvider.ConversationColumns.READ, MessageColumns.FLAG_READ)
|
||||
.add(UIProvider.ConversationColumns.STARRED, MessageColumns.FLAG_FAVORITE)
|
||||
.add(UIProvider.ConversationColumns.FLAGS, CONVERSATION_FLAGS)
|
||||
.add(UIProvider.ConversationColumns.ACCOUNT_URI,
|
||||
uriWithColumn("uiaccount", MessageColumns.ACCOUNT_KEY))
|
||||
.add(UIProvider.ConversationColumns.SENDER_INFO, MessageColumns.FROM_LIST)
|
||||
.build();
|
||||
}
|
||||
return sMessageListMap;
|
||||
}
|
||||
private static ProjectionMap sMessageListMap;
|
||||
|
||||
/**
|
||||
* Generate UIProvider draft type; note the test for "reply all" must come before "reply"
|
||||
|
@ -2167,45 +2182,53 @@ outer:
|
|||
* Mapping of UIProvider columns to EmailProvider columns for a detailed message view in
|
||||
* UnifiedEmail
|
||||
*/
|
||||
private static final ProjectionMap sMessageViewMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, Message.TABLE_NAME + "." + EmailContent.MessageColumns.ID)
|
||||
.add(UIProvider.MessageColumns.SERVER_ID, SyncColumns.SERVER_ID)
|
||||
.add(UIProvider.MessageColumns.URI, uriWithFQId("uimessage", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.CONVERSATION_ID,
|
||||
uriWithFQId("uimessage", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.SUBJECT, EmailContent.MessageColumns.SUBJECT)
|
||||
.add(UIProvider.MessageColumns.SNIPPET, EmailContent.MessageColumns.SNIPPET)
|
||||
.add(UIProvider.MessageColumns.FROM, EmailContent.MessageColumns.FROM_LIST)
|
||||
.add(UIProvider.MessageColumns.TO, EmailContent.MessageColumns.TO_LIST)
|
||||
.add(UIProvider.MessageColumns.CC, EmailContent.MessageColumns.CC_LIST)
|
||||
.add(UIProvider.MessageColumns.BCC, EmailContent.MessageColumns.BCC_LIST)
|
||||
.add(UIProvider.MessageColumns.REPLY_TO, EmailContent.MessageColumns.REPLY_TO_LIST)
|
||||
.add(UIProvider.MessageColumns.DATE_RECEIVED_MS, EmailContent.MessageColumns.TIMESTAMP)
|
||||
.add(UIProvider.MessageColumns.BODY_HTML, Body.HTML_CONTENT)
|
||||
.add(UIProvider.MessageColumns.BODY_TEXT, Body.TEXT_CONTENT)
|
||||
.add(UIProvider.MessageColumns.REF_MESSAGE_ID, "0")
|
||||
.add(UIProvider.MessageColumns.DRAFT_TYPE, NOT_A_DRAFT_STRING)
|
||||
.add(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT, "0")
|
||||
.add(UIProvider.MessageColumns.HAS_ATTACHMENTS, EmailContent.MessageColumns.FLAG_ATTACHMENT)
|
||||
.add(UIProvider.MessageColumns.ATTACHMENT_LIST_URI,
|
||||
uriWithFQId("uiattachments", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.MESSAGE_FLAGS, MESSAGE_FLAGS)
|
||||
.add(UIProvider.MessageColumns.SAVE_MESSAGE_URI,
|
||||
uriWithFQId("uiupdatedraft", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.SEND_MESSAGE_URI,
|
||||
uriWithFQId("uisenddraft", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.DRAFT_TYPE, MESSAGE_DRAFT_TYPE)
|
||||
.add(UIProvider.MessageColumns.MESSAGE_ACCOUNT_URI,
|
||||
uriWithColumn("account", MessageColumns.ACCOUNT_KEY))
|
||||
.add(UIProvider.MessageColumns.STARRED, EmailContent.MessageColumns.FLAG_FAVORITE)
|
||||
.add(UIProvider.MessageColumns.READ, EmailContent.MessageColumns.FLAG_READ)
|
||||
.add(UIProvider.MessageColumns.SPAM_WARNING_STRING, null)
|
||||
.add(UIProvider.MessageColumns.SPAM_WARNING_LEVEL,
|
||||
Integer.toString(UIProvider.SpamWarningLevel.NO_WARNING))
|
||||
.add(UIProvider.MessageColumns.SPAM_WARNING_LINK_TYPE,
|
||||
Integer.toString(UIProvider.SpamWarningLinkType.NO_LINK))
|
||||
.add(UIProvider.MessageColumns.VIA_DOMAIN, null)
|
||||
.build();
|
||||
private ProjectionMap getMessageViewMap() {
|
||||
if (sMessageViewMap == null) {
|
||||
sMessageViewMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, Message.TABLE_NAME + "." + EmailContent.MessageColumns.ID)
|
||||
.add(UIProvider.MessageColumns.SERVER_ID, SyncColumns.SERVER_ID)
|
||||
.add(UIProvider.MessageColumns.URI, uriWithFQId("uimessage", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.CONVERSATION_ID,
|
||||
uriWithFQId("uimessage", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.SUBJECT, EmailContent.MessageColumns.SUBJECT)
|
||||
.add(UIProvider.MessageColumns.SNIPPET, EmailContent.MessageColumns.SNIPPET)
|
||||
.add(UIProvider.MessageColumns.FROM, EmailContent.MessageColumns.FROM_LIST)
|
||||
.add(UIProvider.MessageColumns.TO, EmailContent.MessageColumns.TO_LIST)
|
||||
.add(UIProvider.MessageColumns.CC, EmailContent.MessageColumns.CC_LIST)
|
||||
.add(UIProvider.MessageColumns.BCC, EmailContent.MessageColumns.BCC_LIST)
|
||||
.add(UIProvider.MessageColumns.REPLY_TO, EmailContent.MessageColumns.REPLY_TO_LIST)
|
||||
.add(UIProvider.MessageColumns.DATE_RECEIVED_MS,
|
||||
EmailContent.MessageColumns.TIMESTAMP)
|
||||
.add(UIProvider.MessageColumns.BODY_HTML, Body.HTML_CONTENT)
|
||||
.add(UIProvider.MessageColumns.BODY_TEXT, Body.TEXT_CONTENT)
|
||||
.add(UIProvider.MessageColumns.REF_MESSAGE_ID, "0")
|
||||
.add(UIProvider.MessageColumns.DRAFT_TYPE, NOT_A_DRAFT_STRING)
|
||||
.add(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT, "0")
|
||||
.add(UIProvider.MessageColumns.HAS_ATTACHMENTS,
|
||||
EmailContent.MessageColumns.FLAG_ATTACHMENT)
|
||||
.add(UIProvider.MessageColumns.ATTACHMENT_LIST_URI,
|
||||
uriWithFQId("uiattachments", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.MESSAGE_FLAGS, MESSAGE_FLAGS)
|
||||
.add(UIProvider.MessageColumns.SAVE_MESSAGE_URI,
|
||||
uriWithFQId("uiupdatedraft", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.SEND_MESSAGE_URI,
|
||||
uriWithFQId("uisenddraft", Message.TABLE_NAME))
|
||||
.add(UIProvider.MessageColumns.DRAFT_TYPE, MESSAGE_DRAFT_TYPE)
|
||||
.add(UIProvider.MessageColumns.MESSAGE_ACCOUNT_URI,
|
||||
uriWithColumn("account", MessageColumns.ACCOUNT_KEY))
|
||||
.add(UIProvider.MessageColumns.STARRED, EmailContent.MessageColumns.FLAG_FAVORITE)
|
||||
.add(UIProvider.MessageColumns.READ, EmailContent.MessageColumns.FLAG_READ)
|
||||
.add(UIProvider.MessageColumns.SPAM_WARNING_STRING, null)
|
||||
.add(UIProvider.MessageColumns.SPAM_WARNING_LEVEL,
|
||||
Integer.toString(UIProvider.SpamWarningLevel.NO_WARNING))
|
||||
.add(UIProvider.MessageColumns.SPAM_WARNING_LINK_TYPE,
|
||||
Integer.toString(UIProvider.SpamWarningLinkType.NO_LINK))
|
||||
.add(UIProvider.MessageColumns.VIA_DOMAIN, null)
|
||||
.build();
|
||||
}
|
||||
return sMessageViewMap;
|
||||
}
|
||||
private static ProjectionMap sMessageViewMap;
|
||||
|
||||
/**
|
||||
* Generate UIProvider folder capabilities from mailbox flags
|
||||
|
@ -2236,50 +2259,62 @@ outer:
|
|||
+ " WHEN " + Mailbox.TYPE_STARRED + " THEN " + R.drawable.ic_menu_star_holo_light
|
||||
+ " ELSE -1 END";
|
||||
|
||||
private static final ProjectionMap sFolderListMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, MailboxColumns.ID)
|
||||
.add(UIProvider.FolderColumns.URI, uriWithId("uifolder"))
|
||||
.add(UIProvider.FolderColumns.NAME, "displayName")
|
||||
.add(UIProvider.FolderColumns.HAS_CHILDREN,
|
||||
MailboxColumns.FLAGS + "&" + Mailbox.FLAG_HAS_CHILDREN)
|
||||
.add(UIProvider.FolderColumns.CAPABILITIES, FOLDER_CAPABILITIES)
|
||||
.add(UIProvider.FolderColumns.SYNC_WINDOW, "3")
|
||||
.add(UIProvider.FolderColumns.CONVERSATION_LIST_URI, uriWithId("uimessages"))
|
||||
.add(UIProvider.FolderColumns.CHILD_FOLDERS_LIST_URI, uriWithId("uisubfolders"))
|
||||
.add(UIProvider.FolderColumns.UNREAD_COUNT, MailboxColumns.UNREAD_COUNT)
|
||||
.add(UIProvider.FolderColumns.TOTAL_COUNT, MailboxColumns.MESSAGE_COUNT)
|
||||
.add(UIProvider.FolderColumns.REFRESH_URI, uriWithId("uirefresh"))
|
||||
.add(UIProvider.FolderColumns.SYNC_STATUS, MailboxColumns.UI_SYNC_STATUS)
|
||||
.add(UIProvider.FolderColumns.LAST_SYNC_RESULT, MailboxColumns.UI_LAST_SYNC_RESULT)
|
||||
.add(UIProvider.FolderColumns.TYPE, FOLDER_TYPE)
|
||||
.add(UIProvider.FolderColumns.ICON_RES_ID, FOLDER_ICON)
|
||||
.add(UIProvider.FolderColumns.HIERARCHICAL_DESC, MailboxColumns.HIERARCHICAL_NAME)
|
||||
.build();
|
||||
private ProjectionMap getFolderListMap() {
|
||||
if (sFolderListMap == null) {
|
||||
sFolderListMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, MailboxColumns.ID)
|
||||
.add(UIProvider.FolderColumns.URI, uriWithId("uifolder"))
|
||||
.add(UIProvider.FolderColumns.NAME, "displayName")
|
||||
.add(UIProvider.FolderColumns.HAS_CHILDREN,
|
||||
MailboxColumns.FLAGS + "&" + Mailbox.FLAG_HAS_CHILDREN)
|
||||
.add(UIProvider.FolderColumns.CAPABILITIES, FOLDER_CAPABILITIES)
|
||||
.add(UIProvider.FolderColumns.SYNC_WINDOW, "3")
|
||||
.add(UIProvider.FolderColumns.CONVERSATION_LIST_URI, uriWithId("uimessages"))
|
||||
.add(UIProvider.FolderColumns.CHILD_FOLDERS_LIST_URI, uriWithId("uisubfolders"))
|
||||
.add(UIProvider.FolderColumns.UNREAD_COUNT, MailboxColumns.UNREAD_COUNT)
|
||||
.add(UIProvider.FolderColumns.TOTAL_COUNT, MailboxColumns.MESSAGE_COUNT)
|
||||
.add(UIProvider.FolderColumns.REFRESH_URI, uriWithId("uirefresh"))
|
||||
.add(UIProvider.FolderColumns.SYNC_STATUS, MailboxColumns.UI_SYNC_STATUS)
|
||||
.add(UIProvider.FolderColumns.LAST_SYNC_RESULT, MailboxColumns.UI_LAST_SYNC_RESULT)
|
||||
.add(UIProvider.FolderColumns.TYPE, FOLDER_TYPE)
|
||||
.add(UIProvider.FolderColumns.ICON_RES_ID, FOLDER_ICON)
|
||||
.add(UIProvider.FolderColumns.HIERARCHICAL_DESC, MailboxColumns.HIERARCHICAL_NAME)
|
||||
.build();
|
||||
}
|
||||
return sFolderListMap;
|
||||
}
|
||||
private static ProjectionMap sFolderListMap;
|
||||
|
||||
private static final ProjectionMap sAccountListMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, AccountColumns.ID)
|
||||
.add(UIProvider.AccountColumns.FOLDER_LIST_URI, uriWithId("uifolders"))
|
||||
.add(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI, uriWithId("uiallfolders"))
|
||||
.add(UIProvider.AccountColumns.NAME, AccountColumns.DISPLAY_NAME)
|
||||
.add(UIProvider.AccountColumns.SAVE_DRAFT_URI, uriWithId("uisavedraft"))
|
||||
.add(UIProvider.AccountColumns.SEND_MAIL_URI, uriWithId("uisendmail"))
|
||||
.add(UIProvider.AccountColumns.UNDO_URI,
|
||||
("'content://" + UIProvider.AUTHORITY + "/uiundo'"))
|
||||
.add(UIProvider.AccountColumns.URI, uriWithId("uiaccount"))
|
||||
.add(UIProvider.AccountColumns.SEARCH_URI, uriWithId("uisearch"))
|
||||
// TODO: Is provider version used?
|
||||
.add(UIProvider.AccountColumns.PROVIDER_VERSION, "1")
|
||||
.add(UIProvider.AccountColumns.SYNC_STATUS, "0")
|
||||
.add(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI, uriWithId("uirecentfolders"))
|
||||
.add(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
|
||||
uriWithId("uidefaultrecentfolders"))
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.SIGNATURE, AccountColumns.SIGNATURE)
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS,
|
||||
Integer.toString(UIProvider.SnapHeaderValue.ALWAYS))
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
|
||||
Integer.toString(UIProvider.DefaultReplyBehavior.REPLY))
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, "0")
|
||||
.build();
|
||||
private ProjectionMap getAccountListMap() {
|
||||
if (sAccountListMap == null) {
|
||||
sAccountListMap = ProjectionMap.builder()
|
||||
.add(BaseColumns._ID, AccountColumns.ID)
|
||||
.add(UIProvider.AccountColumns.FOLDER_LIST_URI, uriWithId("uifolders"))
|
||||
.add(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI, uriWithId("uiallfolders"))
|
||||
.add(UIProvider.AccountColumns.NAME, AccountColumns.DISPLAY_NAME)
|
||||
.add(UIProvider.AccountColumns.SAVE_DRAFT_URI, uriWithId("uisavedraft"))
|
||||
.add(UIProvider.AccountColumns.SEND_MAIL_URI, uriWithId("uisendmail"))
|
||||
.add(UIProvider.AccountColumns.UNDO_URI,
|
||||
("'content://" + UIProvider.AUTHORITY + "/uiundo'"))
|
||||
.add(UIProvider.AccountColumns.URI, uriWithId("uiaccount"))
|
||||
.add(UIProvider.AccountColumns.SEARCH_URI, uriWithId("uisearch"))
|
||||
// TODO: Is provider version used?
|
||||
.add(UIProvider.AccountColumns.PROVIDER_VERSION, "1")
|
||||
.add(UIProvider.AccountColumns.SYNC_STATUS, "0")
|
||||
.add(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI, uriWithId("uirecentfolders"))
|
||||
.add(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
|
||||
uriWithId("uidefaultrecentfolders"))
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.SIGNATURE, AccountColumns.SIGNATURE)
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS,
|
||||
Integer.toString(UIProvider.SnapHeaderValue.ALWAYS))
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
|
||||
Integer.toString(UIProvider.DefaultReplyBehavior.REPLY))
|
||||
.add(UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, "0")
|
||||
.build();
|
||||
}
|
||||
return sAccountListMap;
|
||||
}
|
||||
private static ProjectionMap sAccountListMap;
|
||||
|
||||
/**
|
||||
* The "ORDER BY" clause for top level folders
|
||||
|
@ -2298,16 +2333,23 @@ outer:
|
|||
/**
|
||||
* Mapping of UIProvider columns to EmailProvider columns for a message's attachments
|
||||
*/
|
||||
private static final ProjectionMap sAttachmentMap = ProjectionMap.builder()
|
||||
.add(UIProvider.AttachmentColumns.NAME, AttachmentColumns.FILENAME)
|
||||
.add(UIProvider.AttachmentColumns.SIZE, AttachmentColumns.SIZE)
|
||||
.add(UIProvider.AttachmentColumns.URI, uriWithId("uiattachment"))
|
||||
.add(UIProvider.AttachmentColumns.CONTENT_TYPE, AttachmentColumns.MIME_TYPE)
|
||||
.add(UIProvider.AttachmentColumns.STATE, AttachmentColumns.UI_STATE)
|
||||
.add(UIProvider.AttachmentColumns.DESTINATION, AttachmentColumns.UI_DESTINATION)
|
||||
.add(UIProvider.AttachmentColumns.DOWNLOADED_SIZE, AttachmentColumns.UI_DOWNLOADED_SIZE)
|
||||
.add(UIProvider.AttachmentColumns.CONTENT_URI, AttachmentColumns.CONTENT_URI)
|
||||
.build();
|
||||
private ProjectionMap getAttachmentMap() {
|
||||
if (sAttachmentMap == null) {
|
||||
sAttachmentMap = ProjectionMap.builder()
|
||||
.add(UIProvider.AttachmentColumns.NAME, AttachmentColumns.FILENAME)
|
||||
.add(UIProvider.AttachmentColumns.SIZE, AttachmentColumns.SIZE)
|
||||
.add(UIProvider.AttachmentColumns.URI, uriWithId("uiattachment"))
|
||||
.add(UIProvider.AttachmentColumns.CONTENT_TYPE, AttachmentColumns.MIME_TYPE)
|
||||
.add(UIProvider.AttachmentColumns.STATE, AttachmentColumns.UI_STATE)
|
||||
.add(UIProvider.AttachmentColumns.DESTINATION, AttachmentColumns.UI_DESTINATION)
|
||||
.add(UIProvider.AttachmentColumns.DOWNLOADED_SIZE,
|
||||
AttachmentColumns.UI_DOWNLOADED_SIZE)
|
||||
.add(UIProvider.AttachmentColumns.CONTENT_URI, AttachmentColumns.CONTENT_URI)
|
||||
.build();
|
||||
}
|
||||
return sAttachmentMap;
|
||||
}
|
||||
private static ProjectionMap sAttachmentMap;
|
||||
|
||||
/**
|
||||
* Generate the SELECT clause using a specified mapping and the original UI projection
|
||||
|
@ -2469,7 +2511,7 @@ outer:
|
|||
"content://ui.email2.android.com/event/" + msg.mId);
|
||||
}
|
||||
}
|
||||
StringBuilder sb = genSelect(sMessageViewMap, uiProjection, values);
|
||||
StringBuilder sb = genSelect(getMessageViewMap(), uiProjection, values);
|
||||
sb.append(" FROM " + Message.TABLE_NAME + "," + Body.TABLE_NAME + " WHERE " +
|
||||
Body.MESSAGE_KEY + "=" + Message.TABLE_NAME + "." + Message.RECORD_ID + " AND " +
|
||||
Message.TABLE_NAME + "." + Message.RECORD_ID + "=?");
|
||||
|
@ -2484,7 +2526,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQueryMailboxMessages(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sMessageListMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
|
||||
sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + Message.MAILBOX_KEY + "=? ORDER BY " +
|
||||
MessageColumns.TIMESTAMP + " DESC");
|
||||
return sb.toString();
|
||||
|
@ -2501,7 +2543,7 @@ outer:
|
|||
long mailboxId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(UIProvider.ConversationColumns.COLOR, CONVERSATION_COLOR);
|
||||
StringBuilder sb = genSelect(sMessageListMap, uiProjection, values);
|
||||
StringBuilder sb = genSelect(getMessageListMap(), uiProjection, values);
|
||||
if (isCombinedMailbox(mailboxId)) {
|
||||
switch (getVirtualMailboxType(mailboxId)) {
|
||||
case Mailbox.TYPE_INBOX:
|
||||
|
@ -2543,7 +2585,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQueryConversation(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sMessageListMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
|
||||
sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + Message.RECORD_ID + "=?");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -2555,7 +2597,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQueryAccountMailboxes(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sFolderListMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
|
||||
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
|
||||
"=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
|
||||
" AND " + MailboxColumns.PARENT_KEY + " < 0 ORDER BY ");
|
||||
|
@ -2571,7 +2613,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQueryAccountAllMailboxes(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sFolderListMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
|
||||
// Use a derived column to choose either hierarchicalName or displayName
|
||||
sb.append(", case when " + MailboxColumns.HIERARCHICAL_NAME + " is null then " +
|
||||
MailboxColumns.DISPLAY_NAME + " else " + MailboxColumns.HIERARCHICAL_NAME +
|
||||
|
@ -2590,7 +2632,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQueryRecentMailboxes(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sFolderListMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
|
||||
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
|
||||
"=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
|
||||
" AND " + MailboxColumns.PARENT_KEY + " < 0 AND " +
|
||||
|
@ -2656,7 +2698,7 @@ outer:
|
|||
getFolderCapabilities(info, mailbox.mFlags, mailbox.mType, mailboxId));
|
||||
}
|
||||
}
|
||||
StringBuilder sb = genSelect(sFolderListMap, uiProjection, values);
|
||||
StringBuilder sb = genSelect(getFolderListMap(), uiProjection, values);
|
||||
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ID + "=?");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -2778,7 +2820,7 @@ outer:
|
|||
values.put(UIProvider.AccountColumns.SettingsColumns.PRIORITY_ARROWS_ENABLED, "0");
|
||||
}
|
||||
|
||||
final StringBuilder sb = genSelect(sAccountListMap, uiProjection, values);
|
||||
final StringBuilder sb = genSelect(getAccountListMap(), uiProjection, values);
|
||||
sb.append(" FROM " + Account.TABLE_NAME + " WHERE " + AccountColumns.ID + "=?");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -3037,7 +3079,7 @@ outer:
|
|||
*/
|
||||
private String genQueryAttachments(String[] uiProjection,
|
||||
List<String> contentTypeQueryParameters) {
|
||||
StringBuilder sb = genSelect(sAttachmentMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getAttachmentMap(), uiProjection);
|
||||
sb.append(" FROM " + Attachment.TABLE_NAME + " WHERE " + AttachmentColumns.MESSAGE_KEY +
|
||||
" =? ");
|
||||
|
||||
|
@ -3069,7 +3111,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQueryAttachment(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sAttachmentMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getAttachmentMap(), uiProjection);
|
||||
sb.append(" FROM " + Attachment.TABLE_NAME + " WHERE " + AttachmentColumns.ID + " =? ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -3081,7 +3123,7 @@ outer:
|
|||
* @return the SQLite query to be executed on the EmailProvider database
|
||||
*/
|
||||
private String genQuerySubfolders(String[] uiProjection) {
|
||||
StringBuilder sb = genSelect(sFolderListMap, uiProjection);
|
||||
StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
|
||||
sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.PARENT_KEY +
|
||||
" =? ORDER BY ");
|
||||
sb.append(MAILBOX_ORDER_BY);
|
||||
|
@ -3157,7 +3199,7 @@ outer:
|
|||
final String[] idAndType = { BaseColumns._ID, UIProvider.FolderColumns.TYPE };
|
||||
|
||||
// Sent, Drafts, and Starred are the default recents.
|
||||
final StringBuilder sb = genSelect(sFolderListMap, idAndType);
|
||||
final StringBuilder sb = genSelect(getFolderListMap(), idAndType);
|
||||
sb.append(" FROM " + Mailbox.TABLE_NAME
|
||||
+ " WHERE " + MailboxColumns.ACCOUNT_KEY + " = " + id
|
||||
+ " AND "
|
||||
|
|
|
@ -203,7 +203,7 @@ public class EmailBroadcastProcessorService extends IntentService {
|
|||
|
||||
private void onSystemAccountChanged() {
|
||||
Log.i(Logging.LOG_TAG, "System accounts updated.");
|
||||
MailService.reconcilePopImapAccountsSync(this);
|
||||
MailService.reconcilePopAccountsSync(this);
|
||||
|
||||
// Start any remote services
|
||||
EmailServiceUtils.startRemoteServices(this);
|
||||
|
|
|
@ -28,11 +28,11 @@ import android.text.TextUtils;
|
|||
import android.util.Log;
|
||||
|
||||
import com.android.email.NotificationController;
|
||||
import com.android.email.R;
|
||||
import com.android.email.mail.Sender;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.provider.Utilities;
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
import com.android.emailcommon.AccountManagerTypes;
|
||||
import com.android.emailcommon.Api;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.TrafficFlags;
|
||||
|
@ -108,7 +108,7 @@ public abstract class EmailServiceStub extends IEmailService.Stub implements IEm
|
|||
Account account = Account.restoreAccountWithId(mContext, mailbox.mAccountKey);
|
||||
if (account == null) return;
|
||||
android.accounts.Account acct = new android.accounts.Account(account.mEmailAddress,
|
||||
AccountManagerTypes.TYPE_POP_IMAP);
|
||||
mContext.getString(R.string.account_manager_type_pop3));
|
||||
Bundle extras = new Bundle();
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
|
||||
extras.putLong(SYNC_EXTRA_MAILBOX_ID, mailboxId);
|
||||
|
@ -450,7 +450,7 @@ public abstract class EmailServiceStub extends IEmailService.Stub implements IEm
|
|||
|
||||
@Override
|
||||
public void deleteAccountPIMData(long accountId) throws RemoteException {
|
||||
MailService.reconcilePopImapAccountsSync(mContext);
|
||||
MailService.reconcilePopAccountsSync(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,21 +21,29 @@ import android.accounts.AccountManagerFuture;
|
|||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.CalendarContract.Calendars;
|
||||
import android.provider.CalendarContract.SyncState;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.SyncStateContract;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.R;
|
||||
|
@ -43,8 +51,8 @@ import com.android.emailcommon.Api;
|
|||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.service.EmailServiceProxy;
|
||||
import com.android.emailcommon.service.IEmailService;
|
||||
import com.android.emailcommon.service.IEmailServiceCallback;
|
||||
|
@ -167,6 +175,7 @@ public class EmailServiceUtils {
|
|||
public CharSequence[] syncIntervals;
|
||||
public int defaultSyncInterval;
|
||||
public String inferPrefix;
|
||||
public boolean requiresAccountUpdate;
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Protocol: ");
|
||||
|
@ -187,7 +196,14 @@ public class EmailServiceUtils {
|
|||
if (info == null) {
|
||||
Log.w(Logging.LOG_TAG, "Returning NullService for " + protocol);
|
||||
return new EmailServiceProxy(context, NullService.class, null);
|
||||
} else if (info.klass != null) {
|
||||
} else {
|
||||
return getServiceFromInfo(context, callback, info);
|
||||
}
|
||||
}
|
||||
|
||||
public static EmailServiceProxy getServiceFromInfo(Context context,
|
||||
IEmailServiceCallback callback, EmailServiceInfo info) {
|
||||
if (info.klass != null) {
|
||||
return new EmailServiceProxy(context, info.klass, callback);
|
||||
} else {
|
||||
return new EmailServiceProxy(context, info.intentAction, callback);
|
||||
|
@ -229,30 +245,47 @@ public class EmailServiceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Change" the account manager type of the account; this entails deleting the account
|
||||
* and adding a new one. We can't call into AccountManager on the UI thread, but we might
|
||||
* well be on it (currently no clean way of guaranteeing that we're not).
|
||||
*
|
||||
* @param context the caller's context
|
||||
* @param amAccount the AccountManager account we're changing
|
||||
* @param newType the new AccountManager type for this account
|
||||
* @param newProtocol the protocol now being used
|
||||
*/
|
||||
private static void updateAccountManagerType(final Context context,
|
||||
final android.accounts.Account amAccount, final String newType,
|
||||
final String newProtocol) {
|
||||
// STOPSHIP There must be a better way
|
||||
Thread amThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateAccountManagerTypeImpl(context, amAccount, newType, newProtocol);
|
||||
}});
|
||||
amThread.start();
|
||||
private static class UpdateAccountManagerTask extends AsyncTask<Void, Void, Void> {
|
||||
private final Context mContext;
|
||||
private final android.accounts.Account mAccount;
|
||||
private final EmailServiceInfo mOldInfo;
|
||||
private final EmailServiceInfo mNewInfo;
|
||||
|
||||
public UpdateAccountManagerTask(Context context, android.accounts.Account amAccount,
|
||||
EmailServiceInfo oldInfo, EmailServiceInfo newInfo) {
|
||||
super();
|
||||
mContext = context;
|
||||
mAccount = amAccount;
|
||||
mOldInfo = oldInfo;
|
||||
mNewInfo = newInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
updateAccountManagerType(mContext, mAccount, mOldInfo, mNewInfo);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateAccountManagerTypeImpl(Context context,
|
||||
android.accounts.Account amAccount, String newType, String newProtocol) {
|
||||
private static class DisableComponentsTask extends AsyncTask<Void, Void, Void> {
|
||||
private final Context mContext;
|
||||
|
||||
public DisableComponentsTask(Context context) {
|
||||
super();
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
disableComponent(mContext, LegacyEmailAuthenticatorService.class);
|
||||
disableComponent(mContext, LegacyEasAuthenticatorService.class);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateAccountManagerType(Context context,
|
||||
android.accounts.Account amAccount, EmailServiceInfo oldInfo,
|
||||
EmailServiceInfo newInfo) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
|
||||
AccountColumns.EMAIL_ADDRESS + "=?", new String[] { amAccount.name }, null);
|
||||
|
@ -260,7 +293,6 @@ public class EmailServiceUtils {
|
|||
if (c == null) return;
|
||||
try {
|
||||
if (c.moveToNext()) {
|
||||
Log.w(Logging.LOG_TAG, "Converting " + amAccount.name + " to " + newProtocol);
|
||||
// Get the EmailProvider Account/HostAuth
|
||||
Account account = new Account();
|
||||
account.restore(c);
|
||||
|
@ -268,6 +300,14 @@ public class EmailServiceUtils {
|
|||
HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
|
||||
if (hostAuth == null) return;
|
||||
|
||||
// Make sure this email address is using the expected protocol; our query to
|
||||
// AccountManager doesn't know which protocol was being used (com.android.email
|
||||
// was used for both pop3 and imap
|
||||
if (!hostAuth.mProtocol.equals(oldInfo.protocol)) {
|
||||
return;
|
||||
}
|
||||
Log.w(Logging.LOG_TAG, "Converting " + amAccount.name + " to " + newInfo.protocol);
|
||||
|
||||
ContentValues accountValues = new ContentValues();
|
||||
int oldFlags = account.mFlags;
|
||||
|
||||
|
@ -280,33 +320,122 @@ public class EmailServiceUtils {
|
|||
// Change the HostAuth to reference the new protocol; this has to be done before
|
||||
// trying to create the AccountManager account (below)
|
||||
ContentValues hostValues = new ContentValues();
|
||||
hostValues.put(HostAuth.PROTOCOL, newProtocol);
|
||||
hostValues.put(HostAuth.PROTOCOL, newInfo.protocol);
|
||||
resolver.update(ContentUris.withAppendedId(HostAuth.CONTENT_URI, hostAuth.mId),
|
||||
hostValues, null, null);
|
||||
Log.w(Logging.LOG_TAG, "Updated HostAuths");
|
||||
|
||||
try {
|
||||
// Get current settings for the existing AccountManager account
|
||||
boolean email = ContentResolver.getSyncAutomatically(amAccount,
|
||||
EmailContent.AUTHORITY);
|
||||
if (!email) {
|
||||
// Try our old provider name
|
||||
email = ContentResolver.getSyncAutomatically(amAccount,
|
||||
"com.android.email.provider");
|
||||
}
|
||||
boolean contacts = ContentResolver.getSyncAutomatically(amAccount,
|
||||
ContactsContract.AUTHORITY);
|
||||
boolean calendar = ContentResolver.getSyncAutomatically(amAccount,
|
||||
CalendarContract.AUTHORITY);
|
||||
Log.w(Logging.LOG_TAG, "Email: " + email + ", Contacts: " + contacts + "," +
|
||||
" Calendar: " + calendar);
|
||||
|
||||
// Delete the AccountManager account
|
||||
AccountManagerFuture<?> amFuture = AccountManager.get(context)
|
||||
.removeAccount(amAccount, null, null);
|
||||
finishAccountManagerBlocker(amFuture);
|
||||
// Get sync keys for calendar/contacts
|
||||
String amName = amAccount.name;
|
||||
String oldType = amAccount.type;
|
||||
ContentProviderClient client = context.getContentResolver()
|
||||
.acquireContentProviderClient(CalendarContract.CONTENT_URI);
|
||||
byte[] calendarSyncKey = null;
|
||||
try {
|
||||
calendarSyncKey = SyncStateContract.Helpers.get(client,
|
||||
asCalendarSyncAdapter(SyncState.CONTENT_URI, amName, oldType),
|
||||
new android.accounts.Account(amName, oldType));
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Logging.LOG_TAG, "Get calendar key FAILED");
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
client = context.getContentResolver()
|
||||
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
|
||||
byte[] contactsSyncKey = null;
|
||||
try {
|
||||
contactsSyncKey = SyncStateContract.Helpers.get(client,
|
||||
ContactsContract.SyncState.CONTENT_URI,
|
||||
new android.accounts.Account(amName, oldType));
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Logging.LOG_TAG, "Get contacts key FAILED");
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
if (calendarSyncKey != null) {
|
||||
Log.w(Logging.LOG_TAG, "Got calendar key: " + new String(calendarSyncKey));
|
||||
}
|
||||
if (contactsSyncKey != null) {
|
||||
Log.w(Logging.LOG_TAG, "Got contacts key: " + new String(contactsSyncKey));
|
||||
}
|
||||
|
||||
// Set up a new AccountManager account with new type and old settings
|
||||
amFuture = MailService.setupAccountManagerAccount(context, account, email,
|
||||
calendar, contacts, null);
|
||||
AccountManagerFuture<?> amFuture = MailService.setupAccountManagerAccount(
|
||||
context, account, email, calendar, contacts, null);
|
||||
finishAccountManagerBlocker(amFuture);
|
||||
Log.w(Logging.LOG_TAG, "Conversion complete!");
|
||||
Log.w(Logging.LOG_TAG, "Created new AccountManager account");
|
||||
|
||||
// Delete the AccountManager account
|
||||
amFuture = AccountManager.get(context)
|
||||
.removeAccount(amAccount, null, null);
|
||||
finishAccountManagerBlocker(amFuture);
|
||||
Log.w(Logging.LOG_TAG, "Deleted old AccountManager account");
|
||||
|
||||
// Restore sync keys for contacts/calendar
|
||||
if (calendarSyncKey != null && calendarSyncKey.length != 0) {
|
||||
client = context.getContentResolver()
|
||||
.acquireContentProviderClient(CalendarContract.CONTENT_URI);
|
||||
try {
|
||||
SyncStateContract.Helpers.set(client,
|
||||
asCalendarSyncAdapter(SyncState.CONTENT_URI, amName,
|
||||
newInfo.accountType),
|
||||
new android.accounts.Account(amName, newInfo.accountType),
|
||||
calendarSyncKey);
|
||||
Log.w(Logging.LOG_TAG, "Set calendar key...");
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Logging.LOG_TAG, "Set calendar key FAILED");
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
if (contactsSyncKey != null && contactsSyncKey.length != 0) {
|
||||
client = context.getContentResolver()
|
||||
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
|
||||
try {
|
||||
SyncStateContract.Helpers.set(client,
|
||||
ContactsContract.SyncState.CONTENT_URI,
|
||||
new android.accounts.Account(amName, newInfo.accountType),
|
||||
contactsSyncKey);
|
||||
Log.w(Logging.LOG_TAG, "Set contacts key...");
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Logging.LOG_TAG, "Set contacts key FAILED");
|
||||
}
|
||||
}
|
||||
|
||||
if (oldInfo.requiresAccountUpdate) {
|
||||
EmailServiceProxy service =
|
||||
EmailServiceUtils.getServiceFromInfo(context, null, newInfo);
|
||||
try {
|
||||
service.serviceUpdated(amAccount.name);
|
||||
Log.w(Logging.LOG_TAG, "Updated account settings");
|
||||
} catch (RemoteException e) {
|
||||
// Old settings won't hurt anyone
|
||||
}
|
||||
}
|
||||
|
||||
// That's all folks!
|
||||
Log.w(Logging.LOG_TAG, "Account update completed.");
|
||||
} finally {
|
||||
// Clear the incomplete flag on the provider account
|
||||
accountValues.put(AccountColumns.FLAGS, oldFlags);
|
||||
resolver.update(accountUri, accountValues, null, null);
|
||||
Log.w(Logging.LOG_TAG, "[Incomplete flag cleared]");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -314,6 +443,14 @@ public class EmailServiceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static void disableComponent(Context context, Class<?> klass) {
|
||||
Log.w(Logging.LOG_TAG, "Disabling legacy authenticator " + klass.getSimpleName());
|
||||
final ComponentName c = new ComponentName(context, klass);
|
||||
context.getPackageManager().setComponentEnabledSetting(c,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse services.xml file to find our available email services
|
||||
*/
|
||||
|
@ -339,12 +476,14 @@ public class EmailServiceUtils {
|
|||
throw new IllegalStateException(
|
||||
"Replacement service not found: " + newProtocol);
|
||||
}
|
||||
info.requiresAccountUpdate = ta.getBoolean(
|
||||
R.styleable.EmailServiceInfo_requiresAccountUpdate, false);
|
||||
AccountManager am = AccountManager.get(context);
|
||||
android.accounts.Account[] amAccounts =
|
||||
am.getAccountsByType(info.accountType);
|
||||
for (android.accounts.Account amAccount: amAccounts) {
|
||||
updateAccountManagerType(context, amAccount, newInfo.accountType,
|
||||
newProtocol);
|
||||
new UpdateAccountManagerTask(context, amAccount, info, newInfo)
|
||||
.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -407,6 +546,8 @@ public class EmailServiceUtils {
|
|||
sServiceList.add(info);
|
||||
}
|
||||
}
|
||||
// Disable our legacy components
|
||||
new DisableComponentsTask(context).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
|
||||
} catch (XmlPullParserException e) {
|
||||
// ignore
|
||||
} catch (IOException e) {
|
||||
|
@ -414,6 +555,12 @@ public class EmailServiceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static Uri asCalendarSyncAdapter(Uri uri, String account, String accountType) {
|
||||
return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
|
||||
.appendQueryParameter(Calendars.ACCOUNT_NAME, account)
|
||||
.appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* A no-op service that can be returned for non-existent/null protocols
|
||||
*/
|
||||
|
@ -504,6 +651,10 @@ public class EmailServiceUtils {
|
|||
public void sendMail(long accountId) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serviceUpdated(String emailAddress) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapabilities(Account acct) throws RemoteException {
|
||||
return 0;
|
||||
|
|
|
@ -19,5 +19,5 @@ package com.android.email.service;
|
|||
/**
|
||||
* This service needs to be declared separately from the base service
|
||||
*/
|
||||
public class Imap2AuthenticatorService extends AuthenticatorService {
|
||||
public class ImapAuthenticatorService extends AuthenticatorService {
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.service;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.email.FixedLengthInputStream;
|
||||
import com.android.email.mail.store.imap.ImapResponse;
|
||||
import com.android.email.mail.store.imap.ImapResponseParser;
|
||||
import com.android.email.mail.store.imap.ImapString;
|
||||
import com.android.emailcommon.Logging;
|
||||
import com.android.emailcommon.TempDirectory;
|
||||
import com.android.emailcommon.utility.Utility;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Subclass of {@link ImapString} used for literals backed by a temp file.
|
||||
*/
|
||||
public class ImapTempFileLiteral extends ImapString {
|
||||
/* package for test */ final File mFile;
|
||||
|
||||
/** Size is purely for toString() */
|
||||
private final int mSize;
|
||||
|
||||
/* package */ ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
|
||||
mSize = stream.getLength();
|
||||
mFile = File.createTempFile("imap", ".tmp", TempDirectory.getTempDirectory());
|
||||
|
||||
// Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
|
||||
// so it'd simply cause a memory leak.
|
||||
// deleteOnExit() simply adds filenames to a static list and the list will never shrink.
|
||||
// mFile.deleteOnExit();
|
||||
OutputStream out = new FileOutputStream(mFile);
|
||||
IOUtils.copy(stream, out);
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure we delete the temp file.
|
||||
*
|
||||
* We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
destroy();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getAsStream() {
|
||||
checkNotDestroyed();
|
||||
try {
|
||||
return new FileInputStream(mFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
// It's probably possible if we're low on storage and the system clears the cache dir.
|
||||
Log.w(Logging.LOG_TAG, "ImapTempFileLiteral: Temp file not found");
|
||||
|
||||
// Return 0 byte stream as a dummy...
|
||||
return new ByteArrayInputStream(new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
checkNotDestroyed();
|
||||
try {
|
||||
byte[] bytes = IOUtils.toByteArray(getAsStream());
|
||||
// Prevent crash from OOM; we've seen this, but only rarely and not reproducibly
|
||||
if (bytes.length > ImapResponseParser.LITERAL_KEEP_IN_MEMORY_THRESHOLD) {
|
||||
throw new IOException();
|
||||
}
|
||||
return Utility.fromAscii(bytes);
|
||||
} catch (IOException e) {
|
||||
Log.w(Logging.LOG_TAG, "ImapTempFileLiteral: Error while reading temp file", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
if (!isDestroyed() && mFile.exists()) {
|
||||
mFile.delete();
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
// Just log and ignore.
|
||||
Log.w(Logging.LOG_TAG, "Failed to remove temp file: " + re.getMessage());
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{%d byte literal(file)}", mSize);
|
||||
}
|
||||
|
||||
public boolean tempFileExistsForTest() {
|
||||
return mFile.exists();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,9 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.emailcommon;
|
||||
package com.android.email.service;
|
||||
|
||||
public class AccountManagerTypes {
|
||||
public static final String TYPE_EXCHANGE = "com.android.exchange";
|
||||
public static final String TYPE_POP_IMAP = "com.android.email";
|
||||
/**
|
||||
* This service needs to be declared separately from the base service
|
||||
*/
|
||||
public class LegacyEasAuthenticatorService extends AuthenticatorService {
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.email.service;
|
||||
|
||||
/**
|
||||
* This service needs to be declared separately from the base service
|
||||
*/
|
||||
public class LegacyEmailAuthenticatorService extends AuthenticatorService {
|
||||
}
|
|
@ -26,11 +26,11 @@ import android.database.Cursor;
|
|||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.android.email.R;
|
||||
import com.android.email.SingleRunningTask;
|
||||
import com.android.email.provider.AccountReconciler;
|
||||
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
|
||||
import com.android.email2.ui.MailActivityEmail;
|
||||
import com.android.emailcommon.AccountManagerTypes;
|
||||
import com.android.emailcommon.provider.Account;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.utility.EmailAsyncTask;
|
||||
|
@ -51,7 +51,7 @@ public class MailService extends Service {
|
|||
EmailAsyncTask.runAsyncParallel(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
reconcilePopImapAccountsSync(MailService.this);
|
||||
reconcilePopAccountsSync(MailService.this);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -70,7 +70,7 @@ public class MailService extends Service {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static ArrayList<Account> getPopImapAccountList(Context context) {
|
||||
public static ArrayList<Account> getPopAccountList(Context context) {
|
||||
ArrayList<Account> providerAccounts = new ArrayList<Account>();
|
||||
Cursor c = context.getContentResolver().query(Account.CONTENT_URI, Account.ID_PROJECTION,
|
||||
null, null, null);
|
||||
|
@ -79,7 +79,8 @@ public class MailService extends Service {
|
|||
long accountId = c.getLong(Account.CONTENT_ID_COLUMN);
|
||||
String protocol = Account.getProtocol(context, accountId);
|
||||
EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
|
||||
if ((info != null) && info.accountType.equals(AccountManagerTypes.TYPE_POP_IMAP)) {
|
||||
if ((info != null) && info.accountType.equals(
|
||||
context.getString(R.string.account_manager_type_pop3))) {
|
||||
Account account = Account.restoreAccountWithId(context, accountId);
|
||||
if (account != null) {
|
||||
providerAccounts.add(account);
|
||||
|
@ -92,13 +93,14 @@ public class MailService extends Service {
|
|||
return providerAccounts;
|
||||
}
|
||||
|
||||
private static final SingleRunningTask<Context> sReconcilePopImapAccountsSyncExecutor =
|
||||
private static final SingleRunningTask<Context> sReconcilePopAccountsSyncExecutor =
|
||||
new SingleRunningTask<Context>("ReconcilePopImapAccountsSync") {
|
||||
@Override
|
||||
protected void runInternal(Context context) {
|
||||
android.accounts.Account[] accountManagerAccounts = AccountManager.get(context)
|
||||
.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
|
||||
ArrayList<Account> providerAccounts = getPopImapAccountList(context);
|
||||
.getAccountsByType(
|
||||
context.getString(R.string.account_manager_type_pop3));
|
||||
ArrayList<Account> providerAccounts = getPopAccountList(context);
|
||||
MailService.reconcileAccountsWithAccountManager(context, providerAccounts,
|
||||
accountManagerAccounts, context);
|
||||
|
||||
|
@ -108,20 +110,8 @@ public class MailService extends Service {
|
|||
/**
|
||||
* Reconcile POP/IMAP accounts.
|
||||
*/
|
||||
public static void reconcilePopImapAccountsSync(Context context) {
|
||||
sReconcilePopImapAccountsSyncExecutor.run(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not POP/IMAP accounts need reconciling or not. This is a safe operation
|
||||
* to perform on the UI thread.
|
||||
*/
|
||||
public static boolean hasMismatchInPopImapAccounts(Context context) {
|
||||
android.accounts.Account[] accountManagerAccounts = AccountManager.get(context)
|
||||
.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
|
||||
ArrayList<Account> providerAccounts = getPopImapAccountList(context);
|
||||
return AccountReconciler.accountsNeedReconciling(
|
||||
context, providerAccounts, accountManagerAccounts);
|
||||
public static void reconcilePopAccountsSync(Context context) {
|
||||
sReconcilePopAccountsSyncExecutor.run(context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,5 +19,5 @@ package com.android.email.service;
|
|||
/**
|
||||
* This service needs to be declared separately from the base service
|
||||
*/
|
||||
public class PopImapAuthenticatorService extends AuthenticatorService {
|
||||
public class Pop3AuthenticatorService extends AuthenticatorService {
|
||||
}
|
|
@ -105,6 +105,11 @@ public class Pop3Service extends Service {
|
|||
// We load attachments during a sync
|
||||
startSync(inboxId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serviceUpdated(String emailAddress) throws RemoteException {
|
||||
// Not required for POP3
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,18 +38,17 @@ import com.android.emailcommon.provider.Account;
|
|||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.emailcommon.provider.EmailContent.AccountColumns;
|
||||
import com.android.emailcommon.provider.EmailContent.Message;
|
||||
import com.android.emailcommon.provider.HostAuth;
|
||||
import com.android.emailcommon.provider.Mailbox;
|
||||
import com.android.emailcommon.service.EmailServiceProxy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PopImapSyncAdapterService extends Service {
|
||||
private static final String TAG = "PopImapSyncAdapterService";
|
||||
public class Pop3SyncAdapterService extends Service {
|
||||
private static final String TAG = "Pop3SyncAdapterService";
|
||||
private static SyncAdapterImpl sSyncAdapter = null;
|
||||
private static final Object sSyncAdapterLock = new Object();
|
||||
|
||||
public PopImapSyncAdapterService() {
|
||||
public Pop3SyncAdapterService() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -65,7 +64,7 @@ public class PopImapSyncAdapterService extends Service {
|
|||
public void onPerformSync(android.accounts.Account account, Bundle extras,
|
||||
String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||
try {
|
||||
PopImapSyncAdapterService.performSync(mContext, account, extras,
|
||||
Pop3SyncAdapterService.performSync(mContext, account, extras,
|
||||
authority, provider, syncResult);
|
||||
} catch (OperationCanceledException e) {
|
||||
}
|
||||
|
@ -87,25 +86,6 @@ public class PopImapSyncAdapterService extends Service {
|
|||
return sSyncAdapter.getSyncAdapterBinder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether or not this mailbox retrieves its data from the server (as opposed to just
|
||||
* a local mailbox that is never synced).
|
||||
*/
|
||||
public static boolean loadsFromServer(Mailbox m, String protocol) {
|
||||
if (HostAuth.LEGACY_SCHEME_IMAP.equals(protocol)) {
|
||||
// TODO: actually use a sync flag when creating the mailboxes. Right now we use an
|
||||
// approximation for IMAP.
|
||||
return m.mType != Mailbox.TYPE_DRAFTS
|
||||
&& m.mType != Mailbox.TYPE_OUTBOX
|
||||
&& m.mType != Mailbox.TYPE_SEARCH;
|
||||
|
||||
} else if (HostAuth.LEGACY_SCHEME_POP3.equals(protocol)) {
|
||||
return Mailbox.TYPE_INBOX == m.mType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void sync(Context context, long mailboxId, SyncResult syncResult,
|
||||
boolean uiRefresh) {
|
||||
TempDirectory.setTempDirectory(context);
|
||||
|
@ -114,8 +94,7 @@ public class PopImapSyncAdapterService extends Service {
|
|||
Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
|
||||
if (account == null) return;
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
String protocol = account.getProtocol(context);
|
||||
if ((mailbox.mType != Mailbox.TYPE_OUTBOX) && !loadsFromServer(mailbox, protocol)) {
|
||||
if ((mailbox.mType != Mailbox.TYPE_OUTBOX) && (mailbox.mType != Mailbox.TYPE_INBOX)) {
|
||||
// This is an update to a message in a non-syncing mailbox; delete this from the
|
||||
// updates table and return
|
||||
resolver.delete(Message.UPDATED_CONTENT_URI, Message.MAILBOX_KEY + "=?",
|
||||
|
@ -134,8 +113,6 @@ public class PopImapSyncAdapterService extends Service {
|
|||
try {
|
||||
if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
|
||||
EmailServiceStub.sendMailImpl(context, account.mId);
|
||||
} else if (protocol.equals(HostAuth.LEGACY_SCHEME_IMAP)) {
|
||||
ImapService.synchronizeMailboxSynchronous(context, account, mailbox);
|
||||
} else {
|
||||
Pop3Service.synchronizeMailboxSynchronous(context, account, mailbox);
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.android.mail.providers.protos.boot;
|
||||
|
||||
import com.android.emailcommon.provider.EmailContent;
|
||||
import com.android.mail.providers.MailAppProvider;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
|
@ -30,12 +31,9 @@ public class AccountReceiver extends BroadcastReceiver {
|
|||
public static final String ACTION_PROVIDER_CREATED
|
||||
= "com.android.email2.providers.protos.boot.intent.ACTION_PROVIDER_CREATED";
|
||||
|
||||
private static final Uri ACCOUNTS_URI =
|
||||
Uri.parse("content://com.android.email.provider/uiaccts");
|
||||
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
MailAppProvider.addAccountsForUriAsync(ACCOUNTS_URI);
|
||||
EmailContent.init(context);
|
||||
MailAppProvider.addAccountsForUriAsync(Uri.parse(EmailContent.CONTENT_URI + "/uiaccts"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,12 +134,12 @@ public class UtilityMediumTests extends ProviderTestCase2<EmailProvider> {
|
|||
|
||||
public void testBuildLimitOneUri() {
|
||||
// EmailProvider supports "?limit="
|
||||
assertEquals(Uri.parse("content://com.android.email.provider?limit=1"),
|
||||
Utility.buildLimitOneUri(Uri.parse("content://com.android.email.provider")));
|
||||
assertEquals(Uri.parse("content://com.android.mail.provider?limit=1"),
|
||||
Utility.buildLimitOneUri(Uri.parse("content://com.android.mail.provider")));
|
||||
|
||||
// Others don't -- so don't add it.
|
||||
assertEquals(Uri.parse("content://com.android.email.attachmentprovider"),
|
||||
Utility.buildLimitOneUri(Uri.parse("content://com.android.email.attachmentprovider"
|
||||
assertEquals(Uri.parse("content://com.android.mail.attachmentprovider"),
|
||||
Utility.buildLimitOneUri(Uri.parse("content://com.android.mail.attachmentprovider"
|
||||
)));
|
||||
assertEquals(Uri.parse("content://gmail-ls/android@gmail.com"),
|
||||
Utility.buildLimitOneUri(Uri.parse("content://gmail-ls/android@gmail.com"
|
||||
|
|
Loading…
Reference in New Issue