ExchangeStore.java revision 22409fcffae4c6e551fb3e6ead4cdc92e33fded1
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.Account;
24import com.android.emailcommon.provider.EmailContent;
25import com.android.emailcommon.provider.HostAuth;
26import com.android.emailcommon.service.EmailServiceProxy;
27import com.android.emailcommon.service.IEmailService;
28
29import android.content.Context;
30import android.os.Bundle;
31import android.os.RemoteException;
32import android.text.TextUtils;
33
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    @SuppressWarnings("hiding")
44    private final ExchangeTransport mTransport;
45
46    /**
47     * Static named constructor.
48     */
49    public static Store newInstance(Account account, Context context,
50            PersistentDataCallbacks callbacks) throws MessagingException {
51        return new ExchangeStore(account, context, callbacks);
52    }
53
54    /**
55     * Creates a new store for the given account.
56     */
57    private ExchangeStore(Account account, Context context, PersistentDataCallbacks callbacks)
58            throws MessagingException {
59        mTransport = ExchangeTransport.getInstance(account, context);
60    }
61
62    @Override
63    public Bundle checkSettings() throws MessagingException {
64        return mTransport.checkSettings();
65    }
66
67    @Override
68    public Folder getFolder(String name) {
69        return null;
70    }
71
72    @Override
73    public Folder[] updateFolders() {
74        return null;
75    }
76
77    /**
78     * Get class of SettingActivity for this Store class.
79     * @return Activity class that has class method actionEditIncomingSettings()
80     */
81    @Override
82    public Class<? extends android.app.Activity> getSettingActivityClass() {
83        return com.android.email.activity.setup.AccountSetupExchange.class;
84    }
85
86    /**
87     * Inform MessagingController that messages sent via EAS will be placed in the Sent folder
88     * automatically (server-side) and don't need to be uploaded.
89     * @return always false for EAS (assuming server-side copy is supported)
90     */
91    @Override
92    public boolean requireCopyMessageToSentFolder() {
93        return false;
94    }
95
96    public static class ExchangeTransport {
97        private final Context mContext;
98        private HostAuth mHostAuth;
99
100        private static final HashMap<Long, ExchangeTransport> sHostAuthToInstanceMap =
101            new HashMap<Long, ExchangeTransport>();
102
103        /**
104         * Public factory.  The transport should be a singleton
105         */
106        public synchronized static ExchangeTransport getInstance(Account account, Context context)
107                throws MessagingException {
108            HostAuth hostAuth = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
109            if (hostAuth == null) {
110                hostAuth = new HostAuth();
111            }
112            final long storeKey = hostAuth.mId;
113            ExchangeTransport transport = sHostAuthToInstanceMap.get(storeKey);
114            if (transport == null) {
115                transport = new ExchangeTransport(account, context);
116                // Only cache a saved HostAuth key
117                if (storeKey != EmailContent.NOT_SAVED) {
118                    sHostAuthToInstanceMap.put(storeKey, transport);
119                }
120            }
121            return transport;
122        }
123
124        /**
125         * Private constructor - use public factory.
126         */
127        private ExchangeTransport(Account account, Context context) throws MessagingException {
128            mContext = context.getApplicationContext();
129            setAccount(account);
130        }
131
132        private void setAccount(final Account account) throws MessagingException {
133            HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
134            if (recvAuth == null || !STORE_SCHEME_EAS.equalsIgnoreCase(recvAuth.mProtocol)) {
135                throw new MessagingException("Unsupported protocol");
136            }
137            if (recvAuth.mAddress == null) {
138                throw new MessagingException("host not specified");
139            }
140            if (!TextUtils.isEmpty(recvAuth.mDomain)) {
141                recvAuth.mDomain = recvAuth.mDomain.substring(1);
142            }
143            recvAuth.mPort = 80;
144            if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) {
145                recvAuth.mPort = 443;
146            }
147
148            String[] userInfoParts = recvAuth.getLogin();
149            if (userInfoParts != null) {
150                if (TextUtils.isEmpty(userInfoParts[0]) || TextUtils.isEmpty(userInfoParts[1])) {
151                    throw new MessagingException("user name and password not specified");
152                }
153            } else {
154                throw new MessagingException("user information not specifed");
155            }
156            mHostAuth = recvAuth;
157        }
158
159        /**
160         * Here's where we check the settings for EAS.
161         * @throws MessagingException if we can't authenticate the account
162         */
163        public Bundle checkSettings() throws MessagingException {
164            try {
165                IEmailService svc = ExchangeUtils.getExchangeService(mContext, null);
166                // Use a longer timeout for the validate command.  Note that the instanceof check
167                // shouldn't be necessary; we'll do it anyway, just to be safe
168                if (svc instanceof EmailServiceProxy) {
169                    ((EmailServiceProxy)svc).setTimeout(90);
170                }
171                return svc.validate(mHostAuth);
172            } catch (RemoteException e) {
173                throw new MessagingException("Call to validate generated an exception", e);
174            }
175        }
176    }
177
178    /**
179     * We handle AutoDiscover for Exchange 2007 (and later) here, wrapping the EmailService call.
180     * The service call returns a HostAuth and we return null if there was a service issue
181     */
182    @Override
183    public Bundle autoDiscover(Context context, String username, String password) {
184        try {
185            return ExchangeUtils.getExchangeService(context, null).autoDiscover(username, password);
186        } catch (RemoteException e) {
187            return null;
188        }
189    }
190}
191