196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/*
296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License.
696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and
1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under the License.
1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email.mail.transport;
1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport com.android.email.mail.Transport;
2096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.util.Log;
2296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
2496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.OutputStream;
26be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadlerimport java.net.InetAddress;
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.ArrayList;
2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.Arrays;
29af6724527e564d35dd27ac35e24dbced554792e5Makoto Onukiimport java.util.regex.Pattern;
30af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
31af6724527e564d35dd27ac35e24dbced554792e5Makoto Onukiimport junit.framework.Assert;
3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/**
3496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * This is a mock Transport that is used to test protocols that use MailTransport.
3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class MockTransport implements Transport {
3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // All flags defining debug or development code settings must be FALSE
3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // when code is checked in or released.
4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static boolean DEBUG_LOG_STREAMS = true;
41a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
4296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static String LOG_TAG = "MockTransport";
4396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
44f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    private static final String SPECIAL_RESPONSE_IOEXCEPTION = "!!!IOEXCEPTION!!!";
45f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki
461a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    private boolean mTlsStarted = false;
47a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean mOpen;
4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean mInputOpen;
5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private int mConnectionSecurity;
51e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    private boolean mTrustCertificates;
52cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler    private String mHost;
53be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    private InetAddress mLocalAddress;
54be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ArrayList<String> mQueuedInput = new ArrayList<String>();
56be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
5796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static class Transaction {
5896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final int ACTION_INJECT_TEXT = 0;
59f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public static final int ACTION_CLIENT_CLOSE = 1;
60f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public static final int ACTION_IO_EXCEPTION = 2;
611a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        public static final int ACTION_START_TLS = 3;
62a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int mAction;
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String mPattern;
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String[] mResponses;
66a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction(String pattern, String[] responses) {
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mAction = ACTION_INJECT_TEXT;
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mPattern = pattern;
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mResponses = responses;
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
72a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction(int otherType) {
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mAction = otherType;
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mPattern = null;
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mResponses = null;
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
78a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public String toString() {
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            switch (mAction) {
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                case ACTION_INJECT_TEXT:
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return mPattern + ": " + Arrays.toString(mResponses);
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                case ACTION_CLIENT_CLOSE:
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return "Expect the client to close";
86f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                case ACTION_IO_EXCEPTION:
87f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                    return "Expect IOException";
881a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler                case ACTION_START_TLS:
891a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler                    return "Expect StartTls";
9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                default:
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return "(Hmm.  Unknown action.)";
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
95a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
9696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ArrayList<Transaction> mPairs = new ArrayList<Transaction>();
97c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler
98c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    /**
99c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     * Give the mock a pattern to wait for.  No response will be sent.
100c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     * @param pattern Java RegEx to wait for
101c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     */
102c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    public void expect(String pattern) {
103c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler        expect(pattern, (String[])null);
104c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    }
105c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler
10696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Give the mock a pattern to wait for and a response to send back.
10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param pattern Java RegEx to wait for
10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param response String to reply with, or null to acccept string but not respond to it
11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expect(String pattern, String response) {
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        expect(pattern, (response == null) ? null : new String[] { response });
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
114a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Give the mock a pattern to wait for and a multi-line response to send back.
11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param pattern Java RegEx to wait for
11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param responses Strings to reply with
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expect(String pattern, String[] responses) {
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction pair = new Transaction(pattern, responses);
12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.add(pair);
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
124af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
125af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    /**
126af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     * Same as {@link #expect(String, String[])}, but the first arg is taken literally, rather than
127af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     * as a regexp.
128af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     */
129af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    public void expectLiterally(String literal, String[] responses) {
130af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        expect("^" + Pattern.quote(literal) + "$", responses);
131af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    }
132af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
133a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki    /**
13496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Tell the Mock Transport that we expect it to be closed.  This will preserve
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the remaining entries in the expect() stream and allow us to "ride over" the close (which
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * would normally reset everything).
13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expectClose() {
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.add(new Transaction(Transaction.ACTION_CLIENT_CLOSE));
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
141a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
142f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    public void expectIOException() {
143f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        mPairs.add(new Transaction(Transaction.ACTION_IO_EXCEPTION));
144f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    }
145f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki
1461a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    public void expectStartTls() {
1471a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        mPairs.add(new Transaction(Transaction.ACTION_START_TLS));
1481a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    }
1491a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler
150f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    private void sendResponse(Transaction pair) {
151f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        switch (pair.mAction) {
152f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            case Transaction.ACTION_INJECT_TEXT:
153f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                for (String s : pair.mResponses) {
154f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                    mQueuedInput.add(s);
155f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                }
156f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                break;
157f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            case Transaction.ACTION_IO_EXCEPTION:
158f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                mQueuedInput.add(SPECIAL_RESPONSE_IOEXCEPTION);
159f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                break;
160f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            default:
161f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                Assert.fail("Invalid action for sendResponse: " + pair.mAction);
16296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
16496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
165e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean canTrySslSecurity() {
167e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        return (mConnectionSecurity == CONNECTION_SECURITY_SSL);
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
169a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
170e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean canTryTlsSecurity() {
172e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        return (mConnectionSecurity == Transport.CONNECTION_SECURITY_TLS);
17396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
174e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler
175e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
176e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    public boolean canTrustAllCertificates() {
177e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        return mTrustCertificates;
178e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    }
179e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler
18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
1811a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler     * Check that TLS was started
1821a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler     */
1831a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    public boolean isTlsStarted() {
1841a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        return mTlsStarted;
1851a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    }
1861a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler
1871a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    /**
188a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     * This simulates a condition where the server has closed its side, causing
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * reads to fail.
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void closeInputStream() {
19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = false;
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
195ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy    @Override
19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void close() {
19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mOpen = false;
19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = false;
19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // unless it was expected as part of a test, reset the stream
20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (mPairs.size() > 0) {
20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Transaction expect = mPairs.remove(0);
20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (expect.mAction == Transaction.ACTION_CLIENT_CLOSE) {
20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mQueuedInput.clear();
20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.clear();
20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
210a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    @Override
211a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    public void setHost(String host) {
212cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler        mHost = host;
213cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler    }
214cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler
215e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String getHost() {
217cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler        return mHost;
21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
220be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    public void setMockLocalAddress(InetAddress address) {
221be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        mLocalAddress = address;
222be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    }
223be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
224e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public InputStream getInputStream() {
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new MockInputStream();
22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This normally serves as a pseudo-clone, for use by Imap.  For the purposes of unit testing,
232a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     * until we need something more complex, we'll just return the actual MockTransport.  Then we
23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * don't have to worry about dealing with test metadata like the expects list or socket state.
23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
235e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
236ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy    public Transport clone() {
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         return this;
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
240e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public OutputStream getOutputStream() {
242af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        Assert.assertTrue(mOpen);
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new MockOutputStream();
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
246a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    @Override
247a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    public void setPort(int port) {
248a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy        SmtpSenderUnitTests.fail("setPort() not implemented");
249a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy    }
250a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy
251e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public int getPort() {
25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.fail("getPort() not implemented");
25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return 0;
25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
257e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public int getSecurity() {
25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return mConnectionSecurity;
26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
262e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean isOpen() {
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return mOpen;
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
267e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void open() /* throws MessagingException, CertificateValidationException */ {
26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mOpen = true;
27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = true;
27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This returns one string (if available) to the caller.  Usually this simply pulls strings
27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * from the mQueuedInput list, but if the list is empty, we also peek the expect list.  This
27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * supports banners, multi-line responses, and any other cases where we respond without
27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * a specific expect pattern.
278a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * If no response text is available, we assert (failing our test) as an underflow.
280a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Logs the read text if DEBUG_LOG_STREAMS is true.
28296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
283e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String readLine() throws IOException {
28596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (!mInputOpen) {
28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            throw new IOException("Reading from MockTransport with closed input");
28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
28996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // if there's nothing to read, see if we can find a null-pattern response
290f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if ((mQueuedInput.size() == 0) && (mPairs.size() > 0)) {
291f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            Transaction pair = mPairs.get(0);
292f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            if (pair.mPattern == null) {
29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mPairs.remove(0);
294f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                sendResponse(pair);
29596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
297f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (mQueuedInput.size() == 0) {
298f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            // MailTransport returns "" at EOS.
299f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            Log.w(LOG_TAG, "Underflow reading from MockTransport");
300f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            return "";
301f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String line = mQueuedInput.remove(0);
30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (DEBUG_LOG_STREAMS) {
30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Log.d(LOG_TAG, "<<< " + line);
30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
306f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (SPECIAL_RESPONSE_IOEXCEPTION.equals(line)) {
307f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            throw new IOException("Expected IOException.");
308f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return line;
31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
312e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void reopenTls() /* throws MessagingException */ {
31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
3151a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        Transaction expect = mPairs.remove(0);
3161a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        SmtpSenderUnitTests.assertTrue(expect.mAction == Transaction.ACTION_START_TLS);
3171a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        mTlsStarted = true;
31896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
31996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
320e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
321e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    public void setSecurity(int connectionSecurity, boolean trustAllCertificates) {
32296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mConnectionSecurity = connectionSecurity;
323e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        mTrustCertificates = trustAllCertificates;
32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
326ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy    @Override
32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */ {
32896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
329a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
33196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Accepts a single string (command or text) that was written by the code under test.
33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Because we are essentially mocking a server, we check to see if this string was expected.
33396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * If the string was expected, we push the corresponding responses into the mQueuedInput
33496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * list, for subsequent calls to readLine().  If the string does not match, we assert
33596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the mismatch.  If no string was expected, we assert it as an overflow.
336a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
33796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Logs the written text if DEBUG_LOG_STREAMS is true.
33896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
339e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
340f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    public void writeLine(String s, String sensitiveReplacement) throws IOException {
34196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (DEBUG_LOG_STREAMS) {
34296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Log.d(LOG_TAG, ">>> " + s);
34396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
34496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
345af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        SmtpSenderUnitTests.assertTrue("Overflow writing to MockTransport: Getting " + s,
346af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki                0 != mPairs.size());
34796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction pair = mPairs.remove(0);
348f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (pair.mAction == Transaction.ACTION_IO_EXCEPTION) {
349f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            throw new IOException("Expected IOException.");
350f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
351af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        SmtpSenderUnitTests.assertTrue("Unexpected string written to MockTransport: Actual=" + s
352af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki                + "  Expected=" + pair.mPattern,
35396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                pair.mPattern != null && s.matches(pair.mPattern));
35496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (pair.mResponses != null) {
355f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            sendResponse(pair);
35696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
35796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
358a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
35996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
36096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is an InputStream that satisfies the needs of getInputStream()
36196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
36296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private class MockInputStream extends InputStream {
36396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
364e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy        private byte[] mNextLine = null;
365e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy        private int mNextIndex = 0;
366a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
36796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
36896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Reads from the same input buffer as readLine()
36996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
37096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
37196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public int read() throws IOException {
37296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (!mInputOpen) {
37396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new IOException();
37496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
375a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
37696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mNextLine != null && mNextIndex < mNextLine.length) {
37796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return mNextLine[mNextIndex++];
37896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
379a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
38096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            // previous line was exhausted so try to get another one
38196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String next = readLine();
38296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (next == null) {
38396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new IOException("Reading from MockTransport with closed input");
38496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
38596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mNextLine = (next + "\r\n").getBytes();
38696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mNextIndex = 0;
38796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
38896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mNextLine != null && mNextIndex < mNextLine.length) {
38996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return mNextLine[mNextIndex++];
39096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
391a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
392a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki            // no joy - throw an exception
393a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki            throw new IOException();
39496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
39596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
396a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
39796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
39896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is an OutputStream that satisfies the needs of getOutputStream()
39996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
40096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private class MockOutputStream extends OutputStream {
40196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
402e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy        private StringBuilder sb = new StringBuilder();
40396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
40496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
405f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public void write(int oneByte) throws IOException {
406c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            // CR or CRLF will immediately dump previous line (w/o CRLF)
407c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            if (oneByte == '\r') {
408c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                writeLine(sb.toString(), null);
409c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                sb = new StringBuilder();
410c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            } else if (oneByte == '\n') {
411c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                // swallow it
412c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            } else {
41396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                sb.append((char)oneByte);
41496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
41596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
41696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
417be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
418e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
419be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    public InetAddress getLocalAddress() {
420be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        if (isOpen()) {
421be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler            return mLocalAddress;
422be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        } else {
423be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler            return null;
424be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        }
425be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    }
426a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy}
427