ExchangeStore.java revision c4cdb11d24c19428dd39f986b00c1a29e75e1505
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.mail.store; 18 19import com.android.email.ExchangeUtils; 20import com.android.email.mail.Store; 21import com.android.emailcommon.mail.Folder; 22import com.android.emailcommon.mail.MessagingException; 23import com.android.emailcommon.provider.EmailContent.Account; 24import com.android.emailcommon.provider.EmailContent.HostAuth; 25import com.android.emailcommon.service.EmailServiceProxy; 26import com.android.emailcommon.service.IEmailService; 27 28import android.content.Context; 29import android.os.Bundle; 30import android.os.RemoteException; 31import android.text.TextUtils; 32 33import java.util.HashMap; 34 35/** 36 * Our Exchange service does not use the sender/store model. This class exists for exactly two 37 * purposes, (1) to provide a hook for checking account connections, and (2) to return 38 * "AccountSetupExchange.class" for getSettingActivityClass(). 39 */ 40public class ExchangeStore extends Store { 41 public static final String LOG_TAG = "ExchangeStore"; 42 43 private final ExchangeTransport mTransport; 44 45 /** 46 * Static named constructor. 47 */ 48 public static Store newInstance(Account account, Context context, 49 PersistentDataCallbacks callbacks) throws MessagingException { 50 return new ExchangeStore(account, context, callbacks); 51 } 52 53 /** 54 * Creates a new store for the given account. 55 */ 56 private ExchangeStore(Account account, Context context, PersistentDataCallbacks callbacks) 57 throws MessagingException { 58 mTransport = ExchangeTransport.getInstance(account, context); 59 } 60 61 @Override 62 public Bundle checkSettings() throws MessagingException { 63 return mTransport.checkSettings(); 64 } 65 66 @Override 67 public Folder getFolder(String name) { 68 return null; 69 } 70 71 @Override 72 public Folder[] updateFolders() { 73 return null; 74 } 75 76 /** 77 * Get class of SettingActivity for this Store class. 78 * @return Activity class that has class method actionEditIncomingSettings() 79 */ 80 @Override 81 public Class<? extends android.app.Activity> getSettingActivityClass() { 82 return com.android.email.activity.setup.AccountSetupExchange.class; 83 } 84 85 /** 86 * Inform MessagingController that this store requires message structures to be prefetched 87 * before it can fetch message bodies (this is due to EAS protocol restrictions.) 88 * @return always true for EAS 89 */ 90 @Override 91 public boolean requireStructurePrefetch() { 92 return true; 93 } 94 95 /** 96 * Inform MessagingController that messages sent via EAS will be placed in the Sent folder 97 * automatically (server-side) and don't need to be uploaded. 98 * @return always false for EAS (assuming server-side copy is supported) 99 */ 100 @Override 101 public boolean requireCopyMessageToSentFolder() { 102 return false; 103 } 104 105 public static class ExchangeTransport { 106 private final Context mContext; 107 private String mHost; 108 private String mDomain; 109 private int mPort; 110 private boolean mSsl; 111 private boolean mTSsl; 112 private String mUsername; 113 private String mPassword; 114 115 private static final HashMap<String, ExchangeTransport> sUriToInstanceMap = 116 new HashMap<String, ExchangeTransport>(); 117 118 /** 119 * Public factory. The transport should be a singleton (per Uri) 120 */ 121 public synchronized static ExchangeTransport getInstance(Account account, Context context) 122 throws MessagingException { 123 final String storeKey = getStoreKey(context, account); 124 ExchangeTransport transport = sUriToInstanceMap.get(storeKey); 125 if (transport == null) { 126 transport = new ExchangeTransport(account, context); 127 sUriToInstanceMap.put(storeKey, transport); 128 } 129 return transport; 130 } 131 132 /** 133 * Private constructor - use public factory. 134 */ 135 private ExchangeTransport(Account account, Context context) throws MessagingException { 136 mContext = context.getApplicationContext(); 137 setAccount(account); 138 } 139 140 private void setAccount(final Account account) throws MessagingException { 141 HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext); 142 if (recvAuth == null || !STORE_SCHEME_EAS.equalsIgnoreCase(recvAuth.mProtocol)) { 143 throw new MessagingException("Unsupported protocol"); 144 } 145 mHost = recvAuth.mAddress; 146 if (mHost == null) { 147 throw new MessagingException("host not specified"); 148 } 149 mDomain = recvAuth.mDomain; 150 if (!TextUtils.isEmpty(mDomain)) { 151 mDomain = mDomain.substring(1); 152 } 153 mPort = 80; 154 if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) { 155 mPort = 443; 156 mSsl = true; 157 } 158 mTSsl = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0); 159 160 String[] userInfoParts = recvAuth.getLogin(); 161 if (userInfoParts != null) { 162 mUsername = userInfoParts[0]; 163 mPassword = userInfoParts[1]; 164 if (TextUtils.isEmpty(mPassword)) { 165 throw new MessagingException("user name and password not specified"); 166 } 167 } else { 168 throw new MessagingException("user information not specifed"); 169 } 170 } 171 172 /** 173 * Here's where we check the settings for EAS. 174 * @throws MessagingException if we can't authenticate the account 175 */ 176 public Bundle checkSettings() throws MessagingException { 177 try { 178 IEmailService svc = ExchangeUtils.getExchangeService(mContext, null); 179 // Use a longer timeout for the validate command. Note that the instanceof check 180 // shouldn't be necessary; we'll do it anyway, just to be safe 181 if (svc instanceof EmailServiceProxy) { 182 ((EmailServiceProxy)svc).setTimeout(90); 183 } 184 return svc.validate("eas", mHost, mUsername, mPassword, mPort, mSsl, mTSsl); 185 } catch (RemoteException e) { 186 throw new MessagingException("Call to validate generated an exception", e); 187 } 188 } 189 } 190 191 /** 192 * We handle AutoDiscover for Exchange 2007 (and later) here, wrapping the EmailService call. 193 * The service call returns a HostAuth and we return null if there was a service issue 194 */ 195 @Override 196 public Bundle autoDiscover(Context context, String username, String password) { 197 try { 198 return ExchangeUtils.getExchangeService(context, null).autoDiscover(username, password); 199 } catch (RemoteException e) { 200 return null; 201 } 202 } 203} 204