1b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu/*
2b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Copyright (C) 2013 The Android Open Source Project
3b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *
4b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Licensed under the Apache License, Version 2.0 (the "License");
5b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * you may not use this file except in compliance with the License.
6b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * You may obtain a copy of the License at
7b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *
8b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *      http://www.apache.org/licenses/LICENSE-2.0
9b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu *
10b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * Unless required by applicable law or agreed to in writing, software
11b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * distributed under the License is distributed on an "AS IS" BASIS,
12b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * See the License for the specific language governing permissions and
14b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu * limitations under the License.
15b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu */
16b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
17b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hupackage com.android.exchange.service;
18b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
1962c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Huimport android.content.ContentResolver;
20328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport android.content.ContentUris;
21c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Huimport android.content.ContentValues;
22b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.content.Context;
23b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.net.Uri;
24b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.os.Build;
2562c287af3bed45818accf595c56879ad5c57aaf9Yu Ping Huimport android.os.Bundle;
26b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.text.TextUtils;
27b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.text.format.DateUtils;
28b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport android.util.Base64;
29b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
30ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Huimport com.android.emailcommon.internet.MimeUtility;
31b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.emailcommon.provider.Account;
32c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Huimport com.android.emailcommon.provider.EmailContent;
33b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.emailcommon.provider.HostAuth;
34b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.emailcommon.provider.Mailbox;
35f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Huimport com.android.emailcommon.service.AccountServiceProxy;
36b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.emailcommon.utility.EmailClientConnectionManager;
37328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Huimport com.android.emailcommon.utility.Utility;
38b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.exchange.Eas;
39b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport com.android.exchange.EasResponse;
409383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Huimport com.android.exchange.eas.EasConnectionCache;
416ac502883abcecc4c80b26ed2b1a2a1e7308505cAlon Albertimport com.android.exchange.utility.CurlLogger;
42a5fd5a343a8698978a88c7d8907de19a07fce2abAnthony Leeimport com.android.exchange.utility.WbxmlResponseLogger;
43c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Huimport com.android.mail.utils.LogUtils;
44b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
45b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.HttpEntity;
46b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.client.HttpClient;
4703735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdonimport org.apache.http.client.methods.HttpGet;
48bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Huimport org.apache.http.client.methods.HttpOptions;
49b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.client.methods.HttpPost;
50b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport org.apache.http.client.methods.HttpUriRequest;
51b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.entity.ByteArrayEntity;
52b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.impl.client.DefaultHttpClient;
53b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.params.BasicHttpParams;
54b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.params.HttpConnectionParams;
55b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport org.apache.http.params.HttpParams;
561d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albertimport org.apache.http.protocol.BasicHttpProcessor;
57b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
58b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport java.io.IOException;
59b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Huimport java.net.URI;
60b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Huimport java.security.cert.CertificateException;
61b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
62b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu/**
63b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu * Base class for communicating with an EAS server. Anything that needs to send messages to the
64b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu * server can subclass this to get access to the {@link #sendHttpClientPost} family of functions.
65c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * TODO: This class has a regrettable name. It's not a connection, but rather a task that happens
66c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu * to have (and use) a connection to the server.
67b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu */
68ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hupublic class EasServerConnection {
69c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu    /** Logging tag. */
70110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert    private static final String TAG = Eas.LOG_TAG;
71c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu
72b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
73b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * Timeout for establishing a connection to the server.
74b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
75b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    private static final long CONNECTION_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS;
76b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
77b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
78b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * Timeout for http requests after the connection has been established.
79b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
80b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    protected static final long COMMAND_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
81b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
82b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    private static final String DEVICE_TYPE = "Android";
83328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    private static final String USER_AGENT = DEVICE_TYPE + '/' + Build.VERSION.RELEASE + '-' +
84b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        Eas.CLIENT_VERSION;
85b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
86ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /** Message MIME type for EAS version 14 and later. */
87ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    private static final String EAS_14_MIME_TYPE = "application/vnd.ms-sync.wbxml";
88b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
89ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
90ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Value for {@link #mStoppedReason} when we haven't been stopped.
91ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
92ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    public static final int STOPPED_REASON_NONE = 0;
93ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
94ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
95ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Passed to {@link #stop} to indicate that this stop request should terminate this task.
96ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
97ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    public static final int STOPPED_REASON_ABORT = 1;
98ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
99ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
100ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Passed to {@link #stop} to indicate that this stop request should restart this task (e.g. in
101ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * order to reload parameters).
102ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
103ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    public static final int STOPPED_REASON_RESTART = 2;
104ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
105328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    private static final String[] ACCOUNT_SECURITY_KEY_PROJECTION =
106328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            { EmailContent.AccountColumns.SECURITY_SYNC_KEY };
107328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
108f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu    private static String sDeviceId = null;
109f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu
110b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    protected final Context mContext;
1110a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    // TODO: Make this private if possible. Subclasses must be careful about altering the HostAuth
1120a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    // to not screw up any connection caching (use redirectHostAuth).
11325b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    protected final HostAuth mHostAuth;
1140a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    protected final Account mAccount;
115328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    private final long mAccountId;
116b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
117b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    // Bookkeeping for interrupting a request. This is primarily for use by Ping (there's currently
118b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    // no mechanism for stopping a sync).
119b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    // Access to these variables should be synchronized on this.
120b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    private HttpUriRequest mPendingRequest = null;
121b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    private boolean mStopped = false;
122ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    private int mStoppedReason = STOPPED_REASON_NONE;
123b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
124b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** The protocol version to use, as a double. */
125ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private double mProtocolVersion = 0.0d;
126b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /** Whether {@link #setProtocolVersion} was last called with a non-null value. */
127b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    private boolean mProtocolVersionIsSet = false;
128d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
129ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
130ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * The client for any requests made by this object. This is created lazily, and cleared
131ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * whenever our host auth is redirected.
132ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
133ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    private HttpClient mClient;
134ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
1353054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu    /**
1363054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu     * This is used only to check when our client needs to be refreshed.
1373054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu     */
1383054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu    private EmailClientConnectionManager mClientConnectionManager;
1393054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu
140ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public EasServerConnection(final Context context, final Account account,
141275e73703e09a1211ae6aa6fc2a43226e1fcdeedMartin Hibdon                               final HostAuth hostAuth) {
142b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        mContext = context;
14325b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        mHostAuth = hostAuth;
1440a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu        mAccount = account;
145328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        mAccountId = account.mId;
146ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        setProtocolVersion(account.mProtocolVersion);
147b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
148b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
149d92a75c707461188e8743149476e8f49ef191b42Tony Mantler    protected EmailClientConnectionManager getClientConnectionManager()
150d92a75c707461188e8743149476e8f49ef191b42Tony Mantler        throws CertificateException {
1513054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu        final EmailClientConnectionManager connManager =
1523054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu                EasConnectionCache.instance().getConnectionManager(mContext, mHostAuth);
1533054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu        if (mClientConnectionManager != connManager) {
1543054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu            mClientConnectionManager = connManager;
1553054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu            mClient = null;
1563054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu        }
1573054f5a5cb02250643af387b9107780fcbc8ac25Yu Ping Hu        return connManager;
15897f38cd6004b54afe9a949658d9962abe8f1f24eYu Ping Hu    }
15997f38cd6004b54afe9a949658d9962abe8f1f24eYu Ping Hu
160ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public void redirectHostAuth(final String newAddress) {
161ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        mClient = null;
16225b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        mHostAuth.mAddress = newAddress;
163c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu        if (mHostAuth.isSaved()) {
1649383babdbd7c0049a0eb238819a5d9737232e8ecYu Ping Hu            EasConnectionCache.instance().uncacheConnectionManager(mHostAuth);
165c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu            final ContentValues cv = new ContentValues(1);
166c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu            cv.put(EmailContent.HostAuthColumns.ADDRESS, newAddress);
167c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu            mHostAuth.update(mContext, cv);
168c516a47f1fd034a3819cd71beca92becf598de8eYu Ping Hu        }
169b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
170b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
171d92a75c707461188e8743149476e8f49ef191b42Tony Mantler    private HttpClient getHttpClient(final long timeout) throws CertificateException {
172ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        if (mClient == null) {
173ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            final HttpParams params = new BasicHttpParams();
174ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            HttpConnectionParams.setConnectionTimeout(params, (int)(CONNECTION_TIMEOUT));
175ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            HttpConnectionParams.setSoTimeout(params, (int)(timeout));
176ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            HttpConnectionParams.setSocketBufferSize(params, 8192);
1771d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert            mClient = new DefaultHttpClient(getClientConnectionManager(), params) {
1789c7165d4c6b90101b781f90b17451efd42a17929Martin Hibdon                @Override
1791d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                protected BasicHttpProcessor createHttpProcessor() {
1801d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                    final BasicHttpProcessor processor = super.createHttpProcessor();
1811d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                    processor.addRequestInterceptor(new CurlLogger());
182a5fd5a343a8698978a88c7d8907de19a07fce2abAnthony Lee                    processor.addResponseInterceptor(new WbxmlResponseLogger());
1831d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                    return processor;
1841d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert                }
1851d800e0ba4820bdff46b6ef2bd16576930033d49Alon Albert            };
186ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
187ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return mClient;
188b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
189b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
19025b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    private String makeAuthString() {
19125b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        final String cs = mHostAuth.mLogin + ":" + mHostAuth.mPassword;
192b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        return "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
193b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
194b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
19525b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    private String makeUserString() {
196f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu        if (sDeviceId == null) {
197f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu            sDeviceId = new AccountServiceProxy(mContext).getDeviceId();
198f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu            if (sDeviceId == null) {
199f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu                LogUtils.e(TAG, "Could not get device id, defaulting to '0'");
200f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu                sDeviceId = "0";
201f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu            }
202b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
20325b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        return "&User=" + Uri.encode(mHostAuth.mLogin) + "&DeviceId=" +
204f928e7f3743c8fa0d00218408fbbbbb000821f34Yu Ping Hu                sDeviceId + "&DeviceType=" + DEVICE_TYPE;
205b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
206b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
20725b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu    private String makeBaseUriString() {
20825b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        return EmailClientConnectionManager.makeScheme(mHostAuth.shouldUseSsl(),
20925b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu                mHostAuth.shouldTrustAllServerCerts(), mHostAuth.mClientCertAlias) +
21025b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu                "://" + mHostAuth.mAddress + "/Microsoft-Server-ActiveSync";
211b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
212b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
213ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public String makeUriString(final String cmd) {
21425b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        String uriString = makeBaseUriString();
215b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (cmd != null) {
21625b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu            uriString += "?Cmd=" + cmd + makeUserString();
217b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
218b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        return uriString;
219b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
220b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
221ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    private String makeUriString(final String cmd, final String extra) {
222ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return makeUriString(cmd) + extra;
223b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
224b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
225b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
226d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     * If a sync causes us to update our protocol version, this function must be called so that
227d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     * subsequent calls to {@link #getProtocolVersion()} will do the right thing.
228b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether the protocol version changed.
229d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     */
230b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public boolean setProtocolVersion(String protocolVersionString) {
231be22ff8b8b3aa6057de165b76da433cabad4dcfdYu Ping Hu        mProtocolVersionIsSet = (protocolVersionString != null);
232ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (protocolVersionString == null) {
233ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            protocolVersionString = Eas.DEFAULT_PROTOCOL_VERSION;
234ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
235b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final double oldProtocolVersion = mProtocolVersion;
236ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        mProtocolVersion = Eas.getProtocolVersionDouble(protocolVersionString);
237b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return (oldProtocolVersion != mProtocolVersion);
238d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    }
239d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
240d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    /**
241ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     * @return The protocol version for this connection.
242d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu     */
243ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public double getProtocolVersion() {
244ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        return mProtocolVersion;
245d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    }
246d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
247d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu    /**
248328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     * @return The useragent string for our client.
249328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu     */
250328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    public final String getUserAgent() {
251328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        return USER_AGENT;
252328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    }
253328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu
254328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu    /**
255ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * Send an http OPTIONS request to server.
256ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @return The {@link EasResponse} from the Exchange server.
257ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @throws IOException
258b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
259d92a75c707461188e8743149476e8f49ef191b42Tony Mantler    protected EasResponse sendHttpClientOptions() throws IOException, CertificateException {
260ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // For OPTIONS, just use the base string and the single header
261ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        final HttpOptions method = new HttpOptions(URI.create(makeBaseUriString()));
26225b95fe9881e51399ea906af6758447d726fb8a5Yu Ping Hu        method.setHeader("Authorization", makeAuthString());
263328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        method.setHeader("User-Agent", getUserAgent());
264ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return EasResponse.fromHttpRequest(getClientConnectionManager(),
265ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu                getHttpClient(COMMAND_TIMEOUT), method);
266ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
267ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
268ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    protected void resetAuthorization(final HttpPost post) {
269ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.removeHeaders("Authorization");
270ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setHeader("Authorization", makeAuthString());
271ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
272ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
273ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
274ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * Make an {@link HttpPost} for a specific request.
275ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param uri The uri for this request, as a {@link String}.
276ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param entity The {@link HttpEntity} for this request.
277ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param contentType The Content-Type for this request.
278ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param usePolicyKey Whether or not a policy key should be sent.
279ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @return
280ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
281ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public HttpPost makePost(final String uri, final HttpEntity entity, final String contentType,
282ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            final boolean usePolicyKey) {
283ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        final HttpPost post = new HttpPost(uri);
284ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setHeader("Authorization", makeAuthString());
285ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        post.setHeader("MS-ASProtocolVersion", String.valueOf(mProtocolVersion));
286328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu        post.setHeader("User-Agent", getUserAgent());
287ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setHeader("Accept-Encoding", "gzip");
288b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee        // If there is no entity, we should not be setting a content-type since this will
289b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee        // result in a 400 from the server in the case of loading an attachment.
290b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee        if (contentType != null && entity != null) {
291ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            post.setHeader("Content-Type", contentType);
292ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
293b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (usePolicyKey) {
294b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            // If there's an account in existence, use its key; otherwise (we're creating the
295b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            // account), send "0".  The server will respond with code 449 if there are policies
296b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            // to be enforced
2970a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            final String key;
298328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            final String accountKey;
299328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            if (mAccountId == Account.NO_ACCOUNT) {
300328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                accountKey = null;
301328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            } else {
302328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu               accountKey = Utility.getFirstRowString(mContext,
303328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        ContentUris.withAppendedId(Account.CONTENT_URI, mAccountId),
304328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu                        ACCOUNT_SECURITY_KEY_PROJECTION, null, null, null, 0);
305328ca0d959f7e729e96f19538c8f3af8fe782a09Yu Ping Hu            }
3060a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            if (!TextUtils.isEmpty(accountKey)) {
3070a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu                key = accountKey;
3080a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu            } else {
3090a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu                key = "0";
310b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            }
311ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            post.setHeader("X-MS-PolicyKey", key);
312b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
313ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        post.setEntity(entity);
314ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return post;
315bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu    }
316bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu
31703735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon    public HttpGet makeGet(final String uri) {
31803735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon        final HttpGet get = new HttpGet(uri);
31903735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon        return get;
32003735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon    }
32103735a839ba61710f887fffd6d3f605c0b127c2eMartin Hibdon
322bc278f1e81ef1d44c28093e830ebe84faa960843Yu Ping Hu    /**
323b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Make an {@link HttpOptions} request for this connection.
324b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return The {@link HttpOptions} object.
325b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
326b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public HttpOptions makeOptions() {
327b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        final HttpOptions method = new HttpOptions(URI.create(makeBaseUriString()));
328b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        method.setHeader("Authorization", makeAuthString());
329b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        method.setHeader("User-Agent", getUserAgent());
330b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return method;
331b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
332b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
333b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
334b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * Send a POST request to the server.
335b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @param cmd The command we're sending to the server.
336b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @param entity The {@link HttpEntity} containing the payload of the message.
337b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @param timeout The timeout for this POST.
338b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @return The response from the Exchange server.
339b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     * @throws IOException
340b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
3410a62e7f88662942c434f629af6495d64dfae7902Yu Ping Hu    protected EasResponse sendHttpClientPost(String cmd, final HttpEntity entity,
342d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            final long timeout) throws IOException, CertificateException {
343b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        final boolean isPingCommand = cmd.equals("Ping");
344b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
345b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // Split the mail sending commands
346e0743ba69a35b525daef103dd273ec1cd1c349f4Martin Hibdon        // TODO: This logic should not be here, the command should be generated correctly
347e0743ba69a35b525daef103dd273ec1cd1c349f4Martin Hibdon        // in a subclass of EasOperation.
348b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        String extra = null;
349b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        boolean msg = false;
350b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
351b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            final int cmdLength = cmd.indexOf('&');
352b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            extra = cmd.substring(cmdLength);
353b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            cmd = cmd.substring(0, cmdLength);
354b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            msg = true;
355b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        } else if (cmd.startsWith("SendMail&")) {
356b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            msg = true;
357b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
358b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
359b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // Send the proper Content-Type header; it's always wbxml except for messages when
360b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // the EAS protocol version is < 14.0
361b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // If entity is null (e.g. for attachments), don't set this header
362ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        final String contentType;
363d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu        if (msg && (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE)) {
364ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            contentType = MimeUtility.MIME_TYPE_RFC822;
365b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        } else if (entity != null) {
366ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            contentType = EAS_14_MIME_TYPE;
367b19dfd15473dfedec2aa481d453b17a24486b05dAnthony Lee        } else {
368ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu            contentType = null;
369ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        }
370ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        final String uriString;
371ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (extra == null) {
372ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            uriString = makeUriString(cmd);
373ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        } else {
374ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            uriString = makeUriString(cmd, extra);
375ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        }
376ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        final HttpPost method = makePost(uriString, entity, contentType, !isPingCommand);
377b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // NOTE
378b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // The next lines are added at the insistence of $VENDOR, who is seeing inappropriate
379b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // network activity related to the Ping command on some networks with some servers.
380b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        // This code should be removed when the underlying issue is resolved
381b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        if (isPingCommand) {
382b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            method.setHeader("Connection", "close");
383b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
384b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return executeHttpUriRequest(method, timeout);
385ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
386b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
387ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public EasResponse sendHttpClientPost(final String cmd, final byte[] bytes,
388d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            final long timeout) throws IOException, CertificateException {
389da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        final ByteArrayEntity entity;
390da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        if (bytes == null) {
391da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu            entity = null;
392da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        } else {
393da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu            entity = new ByteArrayEntity(bytes);
394da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        }
395da835b7580108777bb0fa4a4d7287676a1cd7e53Yu Ping Hu        return sendHttpClientPost(cmd, entity, timeout);
396ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
397ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
398ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    protected EasResponse sendHttpClientPost(final String cmd, final byte[] bytes)
399d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            throws IOException, CertificateException {
400ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        return sendHttpClientPost(cmd, bytes, COMMAND_TIMEOUT);
401ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    }
402ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu
403ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu    /**
404b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Executes an {@link HttpUriRequest}.
405ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * Note: this function must not be called by multiple threads concurrently. Only one thread may
406ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * send server requests from a particular object at a time.
407ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param method The post to execute.
408ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @param timeout The timeout to use.
409ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @return The response from the Exchange server.
410ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     * @throws IOException
411ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu     */
412b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public EasResponse executeHttpUriRequest(final HttpUriRequest method, final long timeout)
413d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            throws IOException, CertificateException {
414dc15b97b8975b994992d14d2cf3e6c8a80645b5dYu Ping Hu        LogUtils.d(TAG, "EasServerConnection about to make request %s", method.getRequestLine());
415ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // The synchronized blocks are here to support the stop() function, specifically to handle
416ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // when stop() is called first. Notably, they are NOT here in order to guard against
417ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu        // concurrent access to this function, which is not supported.
418b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        synchronized (this) {
419b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            if (mStopped) {
420b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu                mStopped = false;
421b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu                // If this gets stopped after the POST actually starts, it throws an IOException.
422b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu                // Therefore if we get stopped here, let's throw the same sort of exception, so
423ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                // callers can equate IOException with "this POST got killed for some reason".
424ddbe7744f17d9dd0e26bc2d5a1e89aec9b031bddYu Ping Hu                throw new IOException("Command was stopped before POST");
425b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            }
426b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu           mPendingRequest = method;
427b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
428ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        boolean postCompleted = false;
429b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        try {
430ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            final EasResponse response = EasResponse.fromHttpRequest(getClientConnectionManager(),
431ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    getHttpClient(timeout), method);
432ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            postCompleted = true;
433ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            return response;
434b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        } finally {
435b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            synchronized (this) {
436b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                mPendingRequest = null;
437ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                if (postCompleted) {
438ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                    mStoppedReason = STOPPED_REASON_NONE;
439ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                }
440b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu            }
441b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
442b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
443b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
444d92a75c707461188e8743149476e8f49ef191b42Tony Mantler    protected EasResponse executePost(final HttpPost method)
445d92a75c707461188e8743149476e8f49ef191b42Tony Mantler            throws IOException, CertificateException {
446b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return executeHttpUriRequest(method, COMMAND_TIMEOUT);
447b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
448b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu
449b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    /**
450ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * If called while this object is executing a POST, interrupt it with an {@link IOException}.
451ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * Otherwise cause the next attempt to execute a POST to be interrupted with an
452ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * {@link IOException}.
453ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * @param reason The reason for requesting a stop. This should be one of the STOPPED_REASON_*
454ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               constants defined in this class, other than {@link #STOPPED_REASON_NONE} which
455ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               is used to signify that no stop has occurred.
456ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               This class simply stores the value; subclasses are responsible for checking
457ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     *               this value when catching the {@link IOException} and responding appropriately.
458b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu     */
459ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public synchronized void stop(final int reason) {
460ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        // Only process legitimate reasons.
461ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu        if (reason >= STOPPED_REASON_ABORT && reason <= STOPPED_REASON_RESTART) {
462b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            final boolean isMidPost = (mPendingRequest != null);
463ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            LogUtils.i(TAG, "%s with reason %d", (isMidPost ? "Interrupt" : "Stop next"), reason);
464ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            mStoppedReason = reason;
465ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            if (isMidPost) {
466b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                mPendingRequest.abort();
467ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            } else {
468ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu                mStopped = true;
469ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu            }
470b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu        }
471ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    }
472ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu
473ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu    /**
474ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     * @return The reason supplied to the last call to {@link #stop}, or
475ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     *         {@link #STOPPED_REASON_NONE} if {@link #stop} hasn't been called since the last
476ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu     *         successful POST.
477ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu     */
478ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu    public synchronized int getStoppedReason() {
479ae6c69d73448bd4184724dfa351d17ca0cf378b2Yu Ping Hu        return mStoppedReason;
480b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu    }
481b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu
482b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu    /**
483b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * Try to register our client certificate, if needed.
484b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return True if we succeeded or didn't need a client cert, false if we failed to register it.
485b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
486b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public boolean registerClientCert() {
487b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        if (mHostAuth.mClientCertAlias != null) {
488b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            try {
489b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                getClientConnectionManager().registerClientCert(mContext, mHostAuth);
490b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            } catch (final CertificateException e) {
491b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                // The client certificate the user specified is invalid/inaccessible.
492b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu                return false;
493b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu            }
494b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        }
495b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return true;
496b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
497b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu
498b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    /**
499b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     * @return Whether {@link #setProtocolVersion} was last called with a non-null value. Note that
500b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     *         at construction time it is set to whatever protocol version is in the account.
501b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu     */
502b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    public boolean isProtocolVersionSet() {
503b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu        return mProtocolVersionIsSet;
504b6ef78e8c4112d47d965b5b274d52fe8885f58faYu Ping Hu    }
505b6503ab0ba4f4e323fb09e6522cd7c898ea3431eYu Ping Hu}
506