am 35177585: Merge "Remove Imap2 from AOSP tree" into jb-ub-mail

* commit '35177585a24b608582536c3d2c6633f8ee486511':
  Remove Imap2 from AOSP tree
This commit is contained in:
Marc Blank 2012-09-24 08:43:15 -07:00 committed by Android Git Automerger
commit ee6ca4e7b2
10 changed files with 0 additions and 3965 deletions

View File

@ -1,21 +0,0 @@
# Copyright 2012, 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.
include $(CLEAR_VARS)
LOCAL_MODULE := Imap2
LOCAL_MODULE_TAGS := optional
include $(BUILD_PHONY_PACKAGE)

View File

@ -1,203 +0,0 @@
/* Copyright (C) 2012 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.imap2;
import android.content.Context;
import android.os.RemoteException;
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.emailcommon.utility.AttachmentUtilities;
import com.android.emailsync.PartRequest;
import com.android.mail.providers.UIProvider;
import org.apache.james.mime4j.decoder.Base64InputStream;
import java.io.Closeable;
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;
/**
* Handle IMAP2 attachment loading
*/
public class AttachmentLoader {
static private final int CHUNK_SIZE = 16*1024;
private final Context mContext;
private final Attachment mAttachment;
private final long mAttachmentId;
private final long mMessageId;
private final Message mMessage;
private final Imap2SyncService mService;
public AttachmentLoader(Imap2SyncService service, PartRequest req) {
mService = service;
mContext = service.mContext;
mAttachment = req.mAttachment;
mAttachmentId = mAttachment.mId;
mMessageId = mAttachment.mMessageKey;
mMessage = Message.restoreMessageWithId(mContext, mMessageId);
}
private void doStatusCallback(int status) {
try {
Imap2SyncManager.callback().loadAttachmentStatus(mMessageId, mAttachmentId, status, 0);
} catch (RemoteException e) {
// No danger if the client is no longer around
}
}
private void doProgressCallback(int progress) {
try {
Imap2SyncManager.callback().loadAttachmentStatus(mMessageId, mAttachmentId,
EmailServiceStatus.IN_PROGRESS, progress);
} catch (RemoteException e) {
// No danger if the client is no longer around
}
}
/**
* Close, ignoring errors (as during cleanup)
* @param c a Closeable
*/
private void close(Closeable c) {
try {
c.close();
} catch (IOException e) {
}
}
/**
* Save away the contentUri for this Attachment and notify listeners
* @throws IOException
*/
private void finishLoadAttachment(File file, OutputStream os) throws IOException {
InputStream in = null;
try {
in = new FileInputStream(file);
if (mAttachment.mEncoding != null &&
"base64".equals(mAttachment.mEncoding.toLowerCase())) {
in = new Base64InputStream(in);
}
AttachmentUtilities.saveAttachment(mContext, in, mAttachment);
doStatusCallback(EmailServiceStatus.SUCCESS);
} catch (FileNotFoundException e) {
// Not bloody likely, as we just created it successfully
throw new IOException("Attachment file not found?");
} finally {
close(in);
}
}
private void readPart (ImapInputStream in, String tag, OutputStream out) throws IOException {
String res = in.readLine();
int bstart = res.indexOf("body[");
if (bstart < 0)
bstart = res.indexOf("BODY[");
if (bstart < 0)
return;
int bend = res.indexOf(']', bstart);
if (bend < 0)
return;
int br = res.indexOf('{');
if (br > 0) {
Parser p = new Parser(res, br + 1);
int expectedLength = p.parseInteger();
int remainingLength = expectedLength;
int totalRead = 0;
byte[] buf = new byte[CHUNK_SIZE];
int lastCallbackPct = -1;
int lastCallbackTotalRead = 0;
while (remainingLength > 0) {
int rdlen = (remainingLength > CHUNK_SIZE ? CHUNK_SIZE : remainingLength);
int bytesRead = in.read(buf, 0, rdlen);
totalRead += bytesRead;
out.write(buf, 0, bytesRead);
remainingLength -= bytesRead;
int pct = (totalRead * 100) / expectedLength;
// Callback only if we've read at least 1% more and have read more than CHUNK_SIZE
// We don't want to spam the Email app
if ((pct > lastCallbackPct) && (totalRead > (lastCallbackTotalRead + CHUNK_SIZE))) {
// Report progress back to the UI
doProgressCallback(pct);
lastCallbackTotalRead = totalRead;
lastCallbackPct = pct;
}
}
out.close();
String line = in.readLine();
if (!line.endsWith(")")) {
mService.errorLog("Bad part?");
throw new IOException();
}
line = in.readLine();
if (!line.startsWith(tag)) {
mService.userLog("Bad part?");
throw new IOException();
}
}
}
/**
* Loads an attachment, based on the PartRequest passed in the constructor
* @throws IOException
*/
public void loadAttachment(Connection conn) throws IOException {
if (mMessage == null) {
doStatusCallback(EmailServiceStatus.MESSAGE_NOT_FOUND);
return;
}
if (mAttachment.mUiState == UIProvider.AttachmentState.SAVED) {
return;
}
// Say we've started loading the attachment
doProgressCallback(0);
try {
OutputStream os = null;
File tmpFile = null;
try {
tmpFile = File.createTempFile("imap_", "tmp", mContext.getCacheDir());
os = new FileOutputStream(tmpFile);
String tag = mService.writeCommand(conn.writer, "uid fetch " + mMessage.mServerId +
" body[" + mAttachment.mLocation + ']');
readPart(conn.reader, tag, os);
finishLoadAttachment(tmpFile, os);
return;
} catch (FileNotFoundException e) {
mService.errorLog("Can't get attachment; write file not found?");
doStatusCallback(EmailServiceStatus.ATTACHMENT_NOT_FOUND);
} finally {
close(os);
if (tmpFile != null) {
tmpFile.delete();
}
}
} catch (IOException e) {
// Report the error, but also report back to the service
doStatusCallback(EmailServiceStatus.CONNECTION_ERROR);
throw e;
} finally {
}
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
import android.accounts.Account;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.database.Cursor;
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 = "Imap EmailSyncAdapterService";
private static SyncAdapterImpl sSyncAdapter = null;
private static final Object sSyncAdapterLock = new Object();
private static final String[] ID_PROJECTION = new String[] {EmailContent.RECORD_ID};
private static final String ACCOUNT_AND_TYPE_INBOX =
MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_INBOX;
public EmailSyncAdapterService() {
super();
}
private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
private Context mContext;
public SyncAdapterImpl(Context context) {
super(context, true /* autoInitialize */);
mContext = context;
}
@Override
public void onPerformSync(Account account, Bundle extras,
String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
EmailSyncAdapterService.performSync(mContext, account, extras,
authority, provider, syncResult);
} catch (OperationCanceledException e) {
}
}
}
@Override
public void onCreate() {
super.onCreate();
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
}
}
}
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
/**
* Partial integration with system SyncManager; we tell our EAS ExchangeService to start an
* inbox sync when we get the signal from the system SyncManager.
*/
private static void performSync(Context context, Account account, Bundle extras,
String authority, ContentProviderClient provider, SyncResult syncResult)
throws OperationCanceledException {
ContentResolver cr = context.getContentResolver();
Log.i(TAG, "performSync");
// Find the (EmailProvider) account associated with this email address
Cursor accountCursor =
cr.query(com.android.emailcommon.provider.Account.CONTENT_URI,
ID_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?", new String[] {account.name},
null);
try {
if (accountCursor.moveToFirst()) {
long accountId = accountCursor.getLong(0);
// Now, find the inbox associated with the account
Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_PROJECTION,
ACCOUNT_AND_TYPE_INBOX, new String[] {Long.toString(accountId)}, null);
try {
if (mailboxCursor.moveToFirst()) {
Log.i(TAG, "Mail sync requested for " + account.name);
// Ask for a sync from our sync manager
//***
//SyncServiceManager.serviceRequest(mailboxCursor.getLong(0),
// SyncServiceManager.SYNC_KICK);
}
} finally {
mailboxCursor.close();
}
}
} finally {
accountCursor.close();
}
}
}

View File

@ -1,377 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
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;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.ProviderUnavailableException;
import com.android.emailcommon.service.AccountServiceProxy;
import com.android.emailcommon.service.EmailServiceCallback;
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.mail.providers.UIProvider;
import com.android.mail.providers.UIProvider.AccountCapabilities;
import com.android.mail.providers.UIProvider.LastSyncResult;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
public class Imap2SyncManager extends SyncManager {
// Callbacks as set up via setCallback
private static final RemoteCallbackList<IEmailServiceCallback> mCallbackList =
new RemoteCallbackList<IEmailServiceCallback>();
private static final EmailServiceCallback sCallbackProxy =
new EmailServiceCallback(mCallbackList);
private Intent mIntent;
private static String PROTOCOL;
/**
* Create our EmailService implementation here.
*/
private final IEmailService.Stub mBinder = new IEmailService.Stub() {
@Override
public int getApiLevel() {
return Api.LEVEL;
}
@Override
public Bundle validate(HostAuth hostAuth) throws RemoteException {
return new Imap2SyncService(Imap2SyncManager.this,
new Mailbox()).validateAccount(hostAuth, Imap2SyncManager.this);
}
@Override
public Bundle autoDiscover(String userName, String password) throws RemoteException {
return null;
}
@Override
public void startSync(long mailboxId, boolean userRequest) throws RemoteException {
SyncManager imapService = INSTANCE;
if (imapService == null) return;
Imap2SyncService svc = (Imap2SyncService) imapService.mServiceMap.get(mailboxId);
if (svc == null) {
startManualSync(mailboxId, userRequest ? SYNC_UI_REQUEST : SYNC_SERVICE_START_SYNC,
null);
} else {
svc.ping();
}
}
@Override
public void stopSync(long mailboxId) throws RemoteException {
stopManualSync(mailboxId);
}
@Override
public void loadAttachment(long attachmentId, boolean background) throws RemoteException {
Attachment att = Attachment.restoreAttachmentWithId(Imap2SyncManager.this, attachmentId);
log("loadAttachment " + attachmentId + ": " + att.mFileName);
sendMessageRequest(new PartRequest(att, null, null));
}
@Override
public void updateFolderList(long accountId) throws RemoteException {
//***
//reloadFolderList(ImapService.this, accountId, false);
}
@Override
public void hostChanged(long accountId) throws RemoteException {
SyncManager exchangeService = INSTANCE;
if (exchangeService == null) return;
ConcurrentHashMap<Long, SyncError> syncErrorMap = exchangeService.mSyncErrorMap;
// Go through the various error mailboxes
for (long mailboxId: syncErrorMap.keySet()) {
SyncError error = syncErrorMap.get(mailboxId);
// If it's a login failure, look a little harder
Mailbox m = Mailbox.restoreMailboxWithId(exchangeService, mailboxId);
// If it's for the account whose host has changed, clear the error
// If the mailbox is no longer around, remove the entry in the map
if (m == null) {
syncErrorMap.remove(mailboxId);
} else if (error != null && m.mAccountKey == accountId) {
error.fatal = false;
error.holdEndTime = 0;
}
}
// Stop any running syncs
exchangeService.stopAccountSyncs(accountId, true);
// Kick ExchangeService
kick("host changed");
}
@Override
public void setLogging(int flags) throws RemoteException {
// Protocol logging
//Eas.setUserDebug(flags);
// Sync logging
setUserDebug(flags);
}
@Override
public void sendMeetingResponse(long messageId, int response) throws RemoteException {
// Not used in IMAP
}
@Override
public void loadMore(long messageId) throws RemoteException {
}
// The following three methods are not implemented in this version
@Override
public boolean createFolder(long accountId, String name) throws RemoteException {
return false;
}
@Override
public boolean deleteFolder(long accountId, String name) throws RemoteException {
return false;
}
@Override
public boolean renameFolder(long accountId, String oldName, String newName)
throws RemoteException {
return false;
}
@Override
public void setCallback(IEmailServiceCallback cb) throws RemoteException {
mCallbackList.register(cb);
}
@Override
public void deleteAccountPIMData(long accountId) throws RemoteException {
// Not required for IMAP
}
@Override
public int searchMessages(long accountId, SearchParams params, long destMailboxId)
throws RemoteException {
SyncManager ssm = INSTANCE;
if (ssm == null) return 0;
Mailbox mailbox = Mailbox.restoreMailboxWithId(ssm, params.mMailboxId);
Imap2SyncService svc = new Imap2SyncService(ssm, mailbox);
setMailboxSyncStatus(destMailboxId, UIProvider.SyncStatus.USER_QUERY);
boolean ioError = false;
try {
return svc.searchMailbox(ssm, accountId, params, destMailboxId);
} catch (IOException e) {
ioError = true;
return 0;
} finally {
// Report ioError status back
setMailboxLastSyncResult(destMailboxId,
ioError ? LastSyncResult.CONNECTION_ERROR : LastSyncResult.SUCCESS);
setMailboxSyncStatus(destMailboxId, UIProvider.SyncStatus.NO_SYNC);
}
}
@Override
public void sendMail(long accountId) throws RemoteException {
// Not required for IMAP
}
@Override
public int getCapabilities(Account acct) throws RemoteException {
return AccountCapabilities.SYNCABLE_FOLDERS |
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() {
return sCallbackProxy;
}
@Override
public AccountObserver getAccountObserver(Handler handler) {
return new AccountObserver(handler) {
@Override
public void newAccount(long acctId) {
// Create the Inbox for the account if it doesn't exist
Context context = getContext();
Account acct = Account.restoreAccountWithId(context, acctId);
if (acct == null) return;
long inboxId = Mailbox.findMailboxOfType(context, acctId, Mailbox.TYPE_INBOX);
if (inboxId != Mailbox.NO_MAILBOX) {
return;
}
Mailbox inbox = new Mailbox();
inbox.mDisplayName = context.getString(R.string.mailbox_name_server_inbox);
inbox.mServerId = "Inbox";
inbox.mAccountKey = acct.mId;
inbox.mType = Mailbox.TYPE_INBOX;
inbox.mSyncInterval = acct.mSyncInterval;
inbox.save(getContext());
log("Creating inbox for account: " + acct.mDisplayName);
Imap2SyncManager.kick("New account");
}
};
}
@Override
public void onStartup() {
// No special behavior
}
private static final String ACCOUNT_KEY_IN = MailboxColumns.ACCOUNT_KEY + " in (";
private String mAccountSelector;
@Override
public String getAccountsSelector() {
if (mAccountSelector == null) {
StringBuilder sb = new StringBuilder(ACCOUNT_KEY_IN);
boolean first = true;
synchronized (mAccountList) {
for (Account account : mAccountList) {
if (!first) {
sb.append(',');
} else {
first = false;
}
sb.append(account.mId);
}
}
sb.append(')');
mAccountSelector = sb.toString();
}
return mAccountSelector;
}
@Override
public AbstractSyncService getServiceForMailbox(Context context, Mailbox mailbox) {
return new Imap2SyncService(context, mailbox);
}
@Override
public AccountList collectAccounts(Context context, AccountList accounts) {
ContentResolver resolver = context.getContentResolver();
Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, null, null,
null);
// 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(PROTOCOL)) {
Account account = new Account();
account.restore(c);
account.mHostAuthRecv = ha;
accounts.add(account);
}
}
}
} finally {
c.close();
}
return accounts;
}
@Override
public String getAccountManagerType() {
return getString(R.string.account_manager_type_imap);
}
@Override
public Intent getServiceIntent() {
if (mIntent == null) {
mIntent = new Intent(this, Imap2SyncManager.class);
}
return mIntent;
}
@Override
public Stub getCallbackProxy() {
return sCallbackProxy;
}
@Override
protected void runAccountReconcilerSync(Context context) {
alwaysLog("Reconciling accounts...");
new AccountServiceProxy(context).reconcileAccounts(
getString(R.string.protocol_imap), getAccountManagerType());
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onStartService(Mailbox mailbox) {
// No special behavior
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,196 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
import android.content.Context;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Base64;
import android.util.Log;
import com.android.emailcommon.Device;
import com.android.emailcommon.Logging;
import com.android.emailcommon.VendorPolicyLoader;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Pattern;
public class ImapId {
private static String sImapId;
/**
* 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 (ImapId.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;
try {
devUID = Device.getDeviceId(context);
} catch (IOException e) {
// This would only happen with file system failure; it's fine to generate one
devUID = "_dev" + System.currentTimeMillis();
}
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();
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ImapInputStream extends FilterInputStream {
public ImapInputStream(InputStream in) {
super(in);
}
public String readLine () throws IOException {
StringBuilder sb = new StringBuilder();
while (true) {
int b = read();
// Line ends with \n; ignore \r
// I'm not sure this is the right thing with a raw \r (no \n following)
if (b < 0)
throw new IOException("Socket closed in readLine");
if (b == '\n')
return sb.toString();
else if (b != '\r') {
sb.append((char)b);
}
}
}
public boolean ready () throws IOException {
return this.available() > 0;
}
}

View File

@ -1,223 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
import java.util.ArrayList;
public class Parser {
String str;
int pos;
int len;
static final String white = "\r\n \t";
public Parser (String _str) {
str = _str;
pos = 0;
len = str.length();
}
public Parser (String _str, int start) {
str = _str;
pos = start;
len = str.length();
}
public void skipWhite () {
while ((pos < len) && white.indexOf(str.charAt(pos)) >= 0)
pos++;
}
public String parseAtom () {
skipWhite();
int start = pos;
while ((pos < len) && white.indexOf(str.charAt(pos)) < 0)
pos++;
if (pos > start)
return str.substring(start, pos);
return null;
}
public char nextChar () {
if (pos >= len)
return 0;
else
return str.charAt(pos++);
}
public char peekChar () {
if (pos >= len)
return 0;
else
return str.charAt(pos);
}
public String parseString () {
return parseString(false);
}
public String parseStringOrAtom () {
return parseString(true);
}
public String parseString (boolean orAtom) {
skipWhite();
char c = nextChar();
if (c != '\"') {
if (c == '{') {
int cnt = parseInteger();
c = nextChar();
if (c != '}')
return null;
int start = pos + 2;
int end = start + cnt;
String s = str.substring(start, end);
pos = end;
return s;
} else if (orAtom) {
backChar();
return parseAtom();
} else if (c == 'n' || c == 'N') {
parseAtom();
return null;
} else
return null;
}
int start = pos;
boolean quote = false;
while (true) {
c = nextChar();
if (c == 0)
return null;
else if (quote)
quote = false;
else if (c == '\\')
quote = true;
else if (c == '\"')
break;
}
return str.substring(start, pos - 1);
}
public void backChar () {
if (pos > 0)
pos--;
}
public String parseListOrNil () {
String list = parseList();
if (list == null) {
parseAtom();
list = "";
}
return list;
}
public String parseList () {
skipWhite();
if (nextChar() != '(') {
backChar();
return null;
}
int start = pos;
int level = 0;
boolean quote = false;
boolean string = false;
while (true) {
char c = nextChar();
if (c == 0) {
return null;
} else if (quote) {
quote = false;
} else if (c == '\\' && string) {
quote = true;
} else if (c == '\"') {
string = !string;
} else if (c == '(' && !string) {
level++;
} else if (c == '{' && !string) {
// Check for string literal
Parser p = new Parser(str, pos);
int cnt = p.parseInteger();
if (cnt > 0 && p.nextChar() == '}') {
pos = p.pos + 2 + cnt;
}
} else if (c == ')' && !string) {
if (level-- == 0)
break;
}
}
return str.substring(start, pos - 1);
}
public int parseInteger () {
skipWhite();
int start = pos;
while (pos < len) {
char c = str.charAt(pos);
if (c >= '0' && c <= '9')
pos++;
else
break;
}
if (pos > start) {
// We know these are positive integers
int sum = 0;
for (int i = start; i < pos; i++) {
sum = (sum * 10) + (str.charAt(i) - '0');
}
return sum;
} else {
return -1;
}
}
public int[] gatherInts () {
int[] list = new int[128];
int size = 128;
int offs = 0;
while (true) {
int i = parseInteger();
if (i >= 0) {
if (offs == size) {
// Double the size of the array as necessary
size <<= 1;
int[] tmp = new int[size];
System.arraycopy(list, 0, tmp, 0, offs);
list = tmp;
}
list[offs++] = i;
}
else
break;
}
int[] res = new int[offs];
System.arraycopy(list, 0, res, 0, offs);
return res;
}
public Integer[] gatherIntegers () {
ArrayList<Integer> list = new ArrayList<Integer>();
while (true) {
Integer i = parseInteger();
if (i >= 0) {
list.add(i);
}
else
break;
}
return list.toArray(new Integer[list.size()]);
}
}

View File

@ -1,121 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
public class QuotedPrintable {
static public String toString (String str) {
int len = str.length();
// Make sure we don't get an index out of bounds error with the = character
int max = len - 2;
StringBuilder sb = new StringBuilder(len);
try {
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
if (c == '=') {
if (i < max) {
char n = str.charAt(++i);
if (n == '\r') {
n = str.charAt(++i);
if (n == '\n')
continue;
else
System.err.println("Not valid QP");
} else {
// Must be less than 0x80, right?
int a;
if (n >= '0' && n <= '9')
a = (n - '0') << 4;
else
a = (10 + (n - 'A')) << 4;
n = str.charAt(++i);
if (n >= '0' && n <= '9')
c = (char) (a + (n - '0'));
else
c = (char) (a + 10 + (n - 'A'));
}
} if (i + 1 == len)
continue;
}
sb.append(c);
}
} catch (IndexOutOfBoundsException e) {
}
String ret = sb.toString();
return ret;
}
static public String encode (String str) {
int len = str.length();
StringBuffer sb = new StringBuffer(len + len>>2);
int i = 0;
while (i < len) {
char c = str.charAt(i++);
if (c < 0x80) {
sb.append(c);
} else {
sb.append('&');
sb.append('#');
sb.append((int)c);
sb.append(';');
}
}
return sb.toString();
}
static public int decode (byte[] bytes, int len) {
// Make sure we don't get an index out of bounds error with the = character
int max = len - 2;
int pos = 0;
try {
for (int i = 0; i < len; i++) {
char c = (char)bytes[i];
if (c == '=') {
if (i < max) {
char n = (char)bytes[++i];
if (n == '\r') {
n = (char)bytes[++i];
if (n == '\n')
continue;
else
System.err.println("Not valid QP");
} else {
// Must be less than 0x80, right?
int a;
if (n >= '0' && n <= '9')
a = (n - '0') << 4;
else
a = (10 + (n - 'A')) << 4;
n = (char)bytes[++i];
if (n >= '0' && n <= '9')
c = (char) (a + (n - '0'));
else
c = (char) (a + 10 + (n - 'A'));
}
} if (i + 1 > len)
continue;
}
bytes[pos++] = (byte)c;
}
} catch (IndexOutOfBoundsException e) {
}
return pos;
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright (C) 2012 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.imap2;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.service.SearchParams;
import com.android.emailsync.Request;
/**
* SearchRequest is the wrapper for server search requests.
*/
public class SearchRequest extends Request {
public final SearchParams mParams;
public SearchRequest(SearchParams _params) {
super(Message.NO_MESSAGE);
mParams = _params;
}
// SearchRequests are unique by their mailboxId
public boolean equals(Object o) {
if (!(o instanceof SearchRequest)) return false;
return ((SearchRequest)o).mParams.mMailboxId == mParams.mMailboxId;
}
public int hashCode() {
return (int)mParams.mMailboxId;
}
}