PageRenderTime 53ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/elbenj/email/mail/store/ExchangeStore.java

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