ExchangeStore.java revision 200c6bd9fa19b78acc2c1664f858521aa9885353
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.email.mail.StoreSynchronizer; 22import com.android.emailcommon.mail.Folder; 23import com.android.emailcommon.mail.MessagingException; 24import com.android.emailcommon.service.EmailServiceProxy; 25import com.android.emailcommon.service.IEmailService; 26 27import android.content.Context; 28import android.os.Bundle; 29import android.os.RemoteException; 30import android.text.TextUtils; 31 32import java.net.URI; 33import java.net.URISyntaxException; 34import java.util.HashMap; 35 36/** 37 * Our Exchange service does not use the sender/store model. This class exists for exactly two 38 * purposes, (1) to provide a hook for checking account connections, and (2) to return 39 * "AccountSetupExchange.class" for getSettingActivityClass(). 40 */ 41public class ExchangeStore extends Store { 42 public static final String LOG_TAG = "ExchangeStore"; 43 44 private final URI mUri; 45 private final ExchangeTransport mTransport; 46 47 /** 48 * Factory method. 49 */ 50 public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) 51 throws MessagingException { 52 return new ExchangeStore(uri, context, callbacks); 53 } 54 55 /** 56 * eas://user:password@server/domain 57 * 58 * @param _uri 59 * @param application 60 */ 61 private ExchangeStore(String _uri, Context context, PersistentDataCallbacks callbacks) 62 throws MessagingException { 63 try { 64 mUri = new URI(_uri); 65 } catch (URISyntaxException e) { 66 throw new MessagingException("Invalid uri for ExchangeStore"); 67 } 68 69 mTransport = ExchangeTransport.getInstance(mUri, context); 70 } 71 72 @Override 73 public Bundle checkSettings() throws MessagingException { 74 return mTransport.checkSettings(mUri); 75 } 76 77 @Override 78 public Folder getFolder(String name) { 79 return null; 80 } 81 82 @Override 83 public Folder[] updateFolders() { 84 return null; 85 } 86 87 /** 88 * Get class of SettingActivity for this Store class. 89 * @return Activity class that has class method actionEditIncomingSettings() 90 */ 91 @Override 92 public Class<? extends android.app.Activity> getSettingActivityClass() { 93 return com.android.email.activity.setup.AccountSetupExchange.class; 94 } 95 96 /** 97 * Get class of sync'er for this Store class. Because exchange Sync rules are so different 98 * than IMAP or POP3, it's likely that an Exchange implementation will need its own sync 99 * controller. If so, this function must return a non-null value. 100 * 101 * @return Message Sync controller, or null to use default 102 */ 103 @Override 104 public StoreSynchronizer getMessageSynchronizer() { 105 return null; 106 } 107 108 /** 109 * Inform MessagingController that this store requires message structures to be prefetched 110 * before it can fetch message bodies (this is due to EAS protocol restrictions.) 111 * @return always true for EAS 112 */ 113 @Override 114 public boolean requireStructurePrefetch() { 115 return true; 116 } 117 118 /** 119 * Inform MessagingController that messages sent via EAS will be placed in the Sent folder 120 * automatically (server-side) and don't need to be uploaded. 121 * @return always false for EAS (assuming server-side copy is supported) 122 */ 123 @Override 124 public boolean requireCopyMessageToSentFolder() { 125 return false; 126 } 127 128 public static class ExchangeTransport { 129 private final Context mContext; 130 131 private String mHost; 132 private String mDomain; 133 private String mUsername; 134 private String mPassword; 135 136 private static final HashMap<String, ExchangeTransport> sUriToInstanceMap = 137 new HashMap<String, ExchangeTransport>(); 138 139 /** 140 * Public factory. The transport should be a singleton (per Uri) 141 */ 142 public synchronized static ExchangeTransport getInstance(URI uri, Context context) 143 throws MessagingException { 144 if (!uri.getScheme().equals("eas") && !uri.getScheme().equals("eas+ssl+") && 145 !uri.getScheme().equals("eas+ssl+trustallcerts")) { 146 throw new MessagingException("Invalid scheme"); 147 } 148 149 final String key = uri.toString(); 150 ExchangeTransport transport = sUriToInstanceMap.get(key); 151 if (transport == null) { 152 transport = new ExchangeTransport(uri, context); 153 sUriToInstanceMap.put(key, transport); 154 } 155 return transport; 156 } 157 158 /** 159 * Private constructor - use public factory. 160 */ 161 private ExchangeTransport(URI uri, Context context) throws MessagingException { 162 mContext = context.getApplicationContext(); 163 setUri(uri); 164 } 165 166 /** 167 * Use the Uri to set up a newly-constructed transport 168 * @param uri 169 * @throws MessagingException 170 */ 171 private void setUri(final URI uri) throws MessagingException { 172 mHost = uri.getHost(); 173 if (mHost == null) { 174 throw new MessagingException("host not specified"); 175 } 176 177 mDomain = uri.getPath(); 178 if (!TextUtils.isEmpty(mDomain)) { 179 mDomain = mDomain.substring(1); 180 } 181 182 final String userInfo = uri.getUserInfo(); 183 if (userInfo == null) { 184 throw new MessagingException("user information not specifed"); 185 } 186 final String[] uinfo = userInfo.split(":", 2); 187 if (uinfo.length != 2) { 188 throw new MessagingException("user name and password not specified"); 189 } 190 mUsername = uinfo[0]; 191 mPassword = uinfo[1]; 192 } 193 194 /** 195 * Here's where we check the settings for EAS. 196 * @param uri the URI of the account to create 197 * @throws MessagingException if we can't authenticate the account 198 */ 199 public Bundle checkSettings(URI uri) throws MessagingException { 200 setUri(uri); 201 boolean ssl = uri.getScheme().contains("+ssl"); 202 boolean tssl = uri.getScheme().contains("+trustallcerts"); 203 try { 204 int port = ssl ? 443 : 80; 205 IEmailService svc = ExchangeUtils.getExchangeService(mContext, null); 206 // Use a longer timeout for the validate command. Note that the instanceof check 207 // shouldn't be necessary; we'll do it anyway, just to be safe 208 if (svc instanceof EmailServiceProxy) { 209 ((EmailServiceProxy)svc).setTimeout(90); 210 } 211 return svc.validate("eas", mHost, mUsername, mPassword, port, ssl, tssl); 212 } catch (RemoteException e) { 213 throw new MessagingException("Call to validate generated an exception", e); 214 } 215 } 216 } 217 218 /** 219 * We handle AutoDiscover for Exchange 2007 (and later) here, wrapping the EmailService call. 220 * The service call returns a HostAuth and we return null if there was a service issue 221 */ 222 @Override 223 public Bundle autoDiscover(Context context, String username, String password) 224 throws MessagingException { 225 try { 226 return ExchangeUtils.getExchangeService(context, null) 227 .autoDiscover(username, password); 228 } catch (RemoteException e) { 229 return null; 230 } 231 } 232} 233