1da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonpackage com.android.exchange.eas;
2c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
33f72ccad244824f81c327d39af026aee1f22f171Martin Hibdonimport android.content.ContentUris;
4c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport android.content.Context;
5c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport android.net.Uri;
6c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport android.text.format.DateUtils;
7adc77ca7aae4d6f50d40379141e4398ca052d5d5Alon Albertimport android.util.Log;
8c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
9c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdonimport com.android.emailcommon.internet.MimeUtility;
10c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.internet.Rfc822Output;
11c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.Account;
12da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonimport com.android.emailcommon.provider.Mailbox;
13c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.Attachment;
14c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.Body;
15c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.BodyColumns;
16c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.MailboxColumns;
17c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.Message;
18c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.MessageColumns;
19c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.provider.EmailContent.SyncColumns;
20c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.emailcommon.utility.Utility;
21da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonimport com.android.exchange.CommandStatusException;
22c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.exchange.Eas;
23c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.exchange.EasResponse;
24da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonimport com.android.exchange.CommandStatusException.CommandStatus;
25da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonimport com.android.exchange.adapter.SendMailParser;
26c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.exchange.adapter.Serializer;
27c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport com.android.exchange.adapter.Tags;
28da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonimport com.android.exchange.adapter.Parser.EmptyStreamException;
2999905866eb53becb2bd2c15f27546d634df9b3b0Tony Mantlerimport com.android.mail.utils.LogUtils;
30c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
31c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport org.apache.http.HttpEntity;
32c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport org.apache.http.HttpStatus;
33c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport org.apache.http.entity.InputStreamEntity;
34c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
35c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.ByteArrayOutputStream;
36c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.File;
37c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.FileInputStream;
38c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.FileNotFoundException;
39c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.FileOutputStream;
40c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.IOException;
41c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.io.OutputStream;
42c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport java.util.ArrayList;
43c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
44da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdonpublic class EasOutboxSync extends EasOperation {
45da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
46c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    // Value for a message's server id when sending fails.
47c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    public static final int SEND_FAILED = 1;
48c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    // This needs to be long enough to send the longest reasonable message, without being so long
49c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    // as to effectively "hang" sending of mail.  The standard 30 second timeout isn't long enough
50c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    // for pictures and the like.  For now, we'll use 15 minutes, in the knowledge that any socket
51c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    // failure would probably generate an Exception before timing out anyway
52c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    public static final long SEND_MAIL_TIMEOUT = 15 * DateUtils.MINUTE_IN_MILLIS;
53c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
54da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    public static final int RESULT_OK = 1;
55da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    public static final int RESULT_IO_ERROR = -100;
56da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    public static final int RESULT_ITEM_NOT_FOUND = -101;
57da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    public static final int RESULT_SEND_FAILED = -102;
58da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
59da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private final Message mMessage;
60da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private final boolean mIsEas14;
61c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    private final File mCacheDir;
62da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private final SmartSendInfo mSmartSendInfo;
63da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private final int mModeTag;
64da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private File mTmpFile;
65da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private FileInputStream mFileStream;
66c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
67da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    public EasOutboxSync(final Context context, final Account account, final Message message,
68da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            final boolean useSmartSend) {
69c35d2fa94faa0f9abeded869a01108bac2bcedccYu Ping Hu        super(context, account);
70da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        mMessage = message;
71da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        mIsEas14 = (Double.parseDouble(mAccount.mProtocolVersion) >=
72da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE);
73c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        mCacheDir = context.getCacheDir();
74da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (useSmartSend) {
75da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            mSmartSendInfo = SmartSendInfo.getSmartSendInfo(mContext, mAccount, mMessage);
76da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } else {
77da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            mSmartSendInfo = null;
78da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
79da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        mModeTag = getModeTag(mSmartSendInfo);
80c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    }
81c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
82da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    @Override
83da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    protected String getCommand() {
84da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        String cmd = "SendMail";
85da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (mSmartSendInfo != null) {
86da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            // In EAS 14, we don't send itemId and collectionId in the command
87da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            if (mIsEas14) {
88da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                cmd = mSmartSendInfo.isForward() ? "SmartForward" : "SmartReply";
89da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            } else {
90da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                cmd = mSmartSendInfo.generateSmartSendCmd();
91da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            }
92da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
93da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        // If we're not EAS 14, add our save-in-sent setting here
94da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (!mIsEas14) {
95da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            cmd += "&SaveInSent=T";
96da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
97da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        return cmd;
98da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    }
99da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
100da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    @Override
101f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon    protected HttpEntity getRequestEntity() throws IOException, MessageInvalidException {
102c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        try {
103da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            mTmpFile = File.createTempFile("eas_", "tmp", mCacheDir);
104da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } catch (final IOException e) {
105da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            LogUtils.w(LOG_TAG, "IO error creating temp file");
106da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            throw new IllegalStateException("Failure creating temp file");
107da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
108da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
109da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (!writeMessageToTempFile(mTmpFile, mMessage, mSmartSendInfo)) {
110f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon            // There are several reasons this could happen, possibly the message is corrupt (e.g.
111f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon            // the To header is null) or the disk is too full to handle the temporary message.
112f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon            // We can't send this message, but we don't want to abort the entire sync. Returning
113f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon            // this error code will let the caller recognize that this operation failed, but we
114f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon            // should continue on with the rest of the sync.
115da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            LogUtils.w(LOG_TAG, "IO error writing to temp file");
116f6158587a963720526d72ba118ad710e21d1c5d2Martin Hibdon            throw new MessageInvalidException("Failure writing to temp file");
117da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
118da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
119da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        try {
120da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            mFileStream = new FileInputStream(mTmpFile);
121da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } catch (final FileNotFoundException e) {
122da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            LogUtils.w(LOG_TAG, "IO error creating fileInputStream");
123da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            throw new IllegalStateException("Failure creating fileInputStream");
124da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
125da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon          final long fileLength = mTmpFile.length();
126da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon          final HttpEntity entity;
127da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon          if (mIsEas14) {
128da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon              entity = new SendMailEntity(mFileStream, fileLength, mModeTag, mMessage,
129da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                      mSmartSendInfo);
130da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon          } else {
131da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon              entity = new InputStreamEntity(mFileStream, fileLength);
132da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon          }
133da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
134da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon          return entity;
135da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    }
136da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
137da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    @Override
138da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    protected int handleHttpError(int httpStatus) {
139da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (httpStatus == HttpStatus.SC_INTERNAL_SERVER_ERROR && mSmartSendInfo != null) {
140da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            // Let's retry without "smart" commands.
141da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            return RESULT_ITEM_NOT_FOUND;
142da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } else {
143da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            return RESULT_OTHER_FAILURE;
144da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
145da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    }
146c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
147da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    @Override
148da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    protected void onRequestMade() {
1493f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon        try {
1503f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon            mFileStream.close();
1513f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon        } catch (IOException e) {
1523f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon            LogUtils.w(LOG_TAG, "IOException closing fileStream %s", e);
1533f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon        }
1543f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon        if (mTmpFile != null && mTmpFile.exists()) {
155da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            mTmpFile.delete();
156da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
157da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    }
158d738c447d8597062f2f20ecb94b96e8bee12a531Yu Ping Hu
159da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    @Override
160da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    protected int handleResponse(EasResponse response) throws IOException, CommandStatusException {
161da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (mIsEas14) {
162da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            try {
163da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // Try to parse the result
164da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                final SendMailParser p = new SendMailParser(response.getInputStream(), mModeTag);
165da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // If we get here, the SendMail failed; go figure
166da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                p.parse();
167da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // The parser holds the status
168da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                final int status = p.getStatus();
169da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                if (CommandStatus.isNeedsProvisioning(status)) {
170da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                    LogUtils.w(LOG_TAG, "Needs provisioning sending mail");
171da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                    return RESULT_PROVISIONING_ERROR;
172da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                } else if (status == CommandStatus.ITEM_NOT_FOUND &&
173da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                        mSmartSendInfo != null) {
174da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                    // Let's retry without "smart" commands.
175da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                    LogUtils.w(LOG_TAG, "Needs provisioning sending mail");
176da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                    return RESULT_ITEM_NOT_FOUND;
177c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                }
178da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
179da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // TODO: Set syncServerId = SEND_FAILED in DB?
180da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                LogUtils.d(LOG_TAG, "General failure sending mail");
181da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                return RESULT_SEND_FAILED;
182da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            } catch (final EmptyStreamException e) {
183da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // This is actually fine; an empty stream means SendMail succeeded
184da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                LogUtils.d(LOG_TAG, "empty response sending mail");
185e573fcd7d1b15d29920760802eb403f9307728dfMartin Hibdon                // Don't return here, fall through so that we'll delete the sent message.
186da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            } catch (final IOException e) {
187da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // Parsing failed in some other way.
188da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                LogUtils.w(LOG_TAG, "IOException sending mail");
189da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                return RESULT_IO_ERROR;
190c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
191da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } else {
192da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            // FLAG: Do we need to parse results for earlier versions?
193da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
1943f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon        mContext.getContentResolver().delete(
1953f72ccad244824f81c327d39af026aee1f22f171Martin Hibdon            ContentUris.withAppendedId(Message.CONTENT_URI, mMessage.mId), null, null);
196da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        return RESULT_OK;
197da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    }
198da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
199da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    /**
200da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon     * Writes message to the temp file.
201da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon     * @param tmpFile The temp file to use.
202da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon     * @param message The {@link Message} to write.
203da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon     * @param smartSendInfo The {@link SmartSendInfo} for this message send attempt.
204da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon     * @return Whether we could successfully write the file.
205da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon     */
206da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private boolean writeMessageToTempFile(final File tmpFile, final Message message,
207da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            final SmartSendInfo smartSendInfo) {
208da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        final FileOutputStream fileStream;
209da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        try {
210da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            fileStream = new FileOutputStream(tmpFile);
211da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            Log.d(LogUtils.TAG, "created outputstream");
212da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } catch (final FileNotFoundException e) {
213da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            Log.e(LogUtils.TAG, "Failed to create message file", e);
214da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            return false;
215da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
216da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        try {
217da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            final boolean smartSend = smartSendInfo != null;
218da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            final ArrayList<Attachment> attachments =
219da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                    smartSend ? smartSendInfo.mRequiredAtts : null;
220da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            Rfc822Output.writeTo(mContext, message, fileStream, smartSend, true, attachments);
221da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        } catch (final Exception e) {
222da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            Log.e(LogUtils.TAG, "Failed to write message file", e);
223da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            return false;
224c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        } finally {
225da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            try {
226da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                fileStream.close();
227da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            } catch (final IOException e) {
228da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                // should not happen
229da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                Log.e(LogUtils.TAG, "Failed to close file - should not happen", e);
230da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            }
231c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
232da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        return true;
233da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    }
234da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon
235da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon    private int getModeTag(final SmartSendInfo smartSendInfo) {
236da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        if (mIsEas14) {
237da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            if (smartSendInfo == null) {
238da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                return Tags.COMPOSE_SEND_MAIL;
239da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            } else if (smartSendInfo.isForward()) {
240da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                return Tags.COMPOSE_SMART_FORWARD;
241da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            } else {
242da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                return Tags.COMPOSE_SMART_REPLY;
243da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon            }
244da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        }
245da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        return 0;
246c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    }
247c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
248c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    /**
249c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu     * Information needed for SmartReply/SmartForward.
250c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu     */
251c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    private static class SmartSendInfo {
252c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public static final String[] BODY_SOURCE_PROJECTION =
253c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                new String[] {BodyColumns.SOURCE_MESSAGE_KEY};
254c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public static final String WHERE_MESSAGE_KEY = Body.MESSAGE_KEY + "=?";
255c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
256c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        final String mItemId;
257c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        final String mCollectionId;
258c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        final boolean mIsReply;
259c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        final ArrayList<Attachment> mRequiredAtts;
260c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
261da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon        private SmartSendInfo(final String itemId, final String collectionId,
262da9fdf76396ca6aa1c8507a6f71336a8bd4145a9Martin Hibdon                final boolean isReply,ArrayList<Attachment> requiredAtts) {
263c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mItemId = itemId;
264c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mCollectionId = collectionId;
265c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mIsReply = isReply;
266c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mRequiredAtts = requiredAtts;
267c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
268c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
269c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public String generateSmartSendCmd() {
270c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final StringBuilder sb = new StringBuilder();
271c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            sb.append(isForward() ? "SmartForward" : "SmartReply");
272c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            sb.append("&ItemId=");
273c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            sb.append(Uri.encode(mItemId, ":"));
274c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            sb.append("&CollectionId=");
275c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            sb.append(Uri.encode(mCollectionId, ":"));
276c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            return sb.toString();
277c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
278c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
279c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public boolean isForward() {
280c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            return !mIsReply;
281c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
282c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
283c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        /**
284c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * See if a given attachment is among an array of attachments; it is if the locations of
285c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * both are the same (we're looking to see if they represent the same attachment on the
286c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * server. Note that an attachment that isn't on the server (e.g. an outbound attachment
287c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * picked from the  gallery) won't have a location, so the result will always be false.
288c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         *
289c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param att the attachment to test
290c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param atts the array of attachments to look in
291c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @return whether the test attachment is among the array of attachments
292c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         */
293c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        private static boolean amongAttachments(final Attachment att, final Attachment[] atts) {
294c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final String location = att.mLocation;
295c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (location == null) return false;
296c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            for (final Attachment a: atts) {
297c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                if (location.equals(a.mLocation)) {
298c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    return true;
299c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                }
300c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
301c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            return false;
302c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
303c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
304c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        /**
305c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * If this message should use SmartReply or SmartForward, return an object with the data
306c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * for the smart send.
307c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         *
308c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param context the caller's context
309c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param account the Account we're sending from
310c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param message the Message being sent
311c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @return an object to support smart sending, or null if not applicable.
312c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         */
313c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public static SmartSendInfo getSmartSendInfo(final Context context,
314c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                final Account account, final Message message) {
315c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final int flags = message.mFlags;
316c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // We only care about the original message if we include quoted text.
317c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if ((flags & Message.FLAG_NOT_INCLUDE_QUOTED_TEXT) != 0) {
318c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                return null;
319c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
320c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final boolean reply = (flags & Message.FLAG_TYPE_REPLY) != 0;
321c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final boolean forward = (flags & Message.FLAG_TYPE_FORWARD) != 0;
322c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // We also only care for replies or forwards.
323c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (!reply && !forward) {
324c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                return null;
325c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
326c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // Just a sanity check here, since we assume that reply and forward are mutually
327c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // exclusive throughout this class.
328c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (reply && forward) {
329c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                return null;
330c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
331c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // If we don't support SmartForward and it's a forward, then don't proceed.
332c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (forward && (account.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) == 0) {
333c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                return null;
334c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
335c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
336c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // Note: itemId and collectionId are the terms used by EAS to refer to the serverId and
337c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // mailboxId of a Message
338c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            String itemId = null;
339c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            String collectionId = null;
340c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
341c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // First, we need to get the id of the reply/forward message
342c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            String[] cols = Utility.getRowColumns(context, Body.CONTENT_URI, BODY_SOURCE_PROJECTION,
343c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    WHERE_MESSAGE_KEY, new String[] {Long.toString(message.mId)});
344c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            long refId = 0;
345b160b48067ff1d6ff937d3e75c182a8102f655adYu Ping Hu            // TODO: We can probably just write a smarter query to do this all at once.
346b160b48067ff1d6ff937d3e75c182a8102f655adYu Ping Hu            if (cols != null && cols[0] != null) {
347c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                refId = Long.parseLong(cols[0]);
348c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                // Then, we need the serverId and mailboxKey of the message
349c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                cols = Utility.getRowColumns(context, Message.CONTENT_URI, refId,
350c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        SyncColumns.SERVER_ID, MessageColumns.MAILBOX_KEY,
351c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        MessageColumns.PROTOCOL_SEARCH_INFO);
352c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                if (cols != null) {
353c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    itemId = cols[0];
354c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    final long boxId = Long.parseLong(cols[1]);
355c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    // Then, we need the serverId of the mailbox
356c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    cols = Utility.getRowColumns(context, Mailbox.CONTENT_URI, boxId,
357c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                            MailboxColumns.SERVER_ID);
358c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    if (cols != null) {
359c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        collectionId = cols[0];
360c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    }
361c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                }
362c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
363c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // We need either a longId or both itemId (serverId) and collectionId (mailboxId) to
364c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // process a smart reply or a smart forward
365c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (itemId != null && collectionId != null) {
366c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                final ArrayList<Attachment> requiredAtts;
367c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                if (forward) {
368c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    // See if we can really smart forward (all reference attachments must be sent)
369c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    final Attachment[] outAtts =
370c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                            Attachment.restoreAttachmentsWithMessageId(context, message.mId);
371c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    final Attachment[] refAtts =
372c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                            Attachment.restoreAttachmentsWithMessageId(context, refId);
373c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    for (final Attachment refAtt: refAtts) {
374c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        // If an original attachment isn't among what's going out, we can't be smart
375c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        if (!amongAttachments(refAtt, outAtts)) {
376c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                            return null;
377c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        }
378c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    }
379c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    requiredAtts = new ArrayList<Attachment>();
380c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    for (final Attachment outAtt: outAtts) {
381c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        // If an outgoing attachment isn't in original message, we must send it
382c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        if (!amongAttachments(outAtt, refAtts)) {
383c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                            requiredAtts.add(outAtt);
384c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        }
385c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    }
386c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                } else {
387c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    requiredAtts = null;
388c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                }
389c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                return new SmartSendInfo(itemId, collectionId, reply, requiredAtts);
390c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
391c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            return null;
392c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
393c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    }
394c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
395c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon    @Override
396c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon    public String getRequestContentType() {
397c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon        // When using older protocols, we need to use a different MIME type for sending messages.
398c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon        if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE) {
399c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon            return MimeUtility.MIME_TYPE_RFC822;
400c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon        } else {
401c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon            return super.getRequestContentType();
402c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon        }
403c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon    }
404c60938ced3be4837f0f9350f614b2eb40a5417f5Martin Hibdon
405c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    /**
406c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu     * Our own HttpEntity subclass that is able to insert opaque data (in this case the MIME
407c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu     * representation of the message body as stored in a temporary file) into the serializer stream
408c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu     */
409c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    private static class SendMailEntity extends InputStreamEntity {
410c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        private final FileInputStream mFileStream;
411c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        private final long mFileLength;
412c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        private final int mSendTag;
413c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        private final Message mMessage;
414c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        private final SmartSendInfo mSmartSendInfo;
415c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
416c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public SendMailEntity(final FileInputStream instream, final long length, final int tag,
417c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                final Message message, final SmartSendInfo smartSendInfo) {
418c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            super(instream, length);
419c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mFileStream = instream;
420c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mFileLength = length;
421c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mSendTag = tag;
422c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mMessage = message;
423c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            mSmartSendInfo = smartSendInfo;
424c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
425c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
426c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        /**
427c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * We always return -1 because we don't know the actual length of the POST data (this
428c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * causes HttpClient to send the data in "chunked" mode)
429c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         */
430c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        @Override
431c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public long getContentLength() {
432c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
433c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            try {
434c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                // Calculate the overhead for the WBXML data
435c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                writeTo(baos, false);
436c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                // Return the actual size that will be sent
437c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                return baos.size() + mFileLength;
438c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            } catch (final IOException e) {
439c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                // Just return -1 (unknown)
440c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            } finally {
441c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                try {
442c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    baos.close();
443c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                } catch (final IOException e) {
444c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    // Ignore
445c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                }
446c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
447c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            return -1;
448c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
449c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
450c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        @Override
451c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public void writeTo(final OutputStream outstream) throws IOException {
452c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            writeTo(outstream, true);
453c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
454c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
455c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        /**
456c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * Write the message to the output stream
457c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param outstream the output stream to write
458c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @param withData whether or not the actual data is to be written; true when sending
459c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         *   mail; false when calculating size only
460c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         * @throws IOException
461c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu         */
462c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        public void writeTo(final OutputStream outstream, final boolean withData)
463c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                throws IOException {
464c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // Not sure if this is possible; the check is taken from the superclass
465c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (outstream == null) {
466c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                throw new IllegalArgumentException("Output stream may not be null");
467c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
468c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
469c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // We'll serialize directly into the output stream
470c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            final Serializer s = new Serializer(outstream);
471c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // Send the appropriate initial tag
472c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            s.start(mSendTag);
473c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // The Message-Id for this message (note that we cannot use the messageId stored in
474c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // the message, as EAS 14 limits the length to 40 chars and we use 70+)
475c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            s.data(Tags.COMPOSE_CLIENT_ID, "SendMail-" + System.nanoTime());
476c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // We always save sent mail
477c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            s.tag(Tags.COMPOSE_SAVE_IN_SENT_ITEMS);
478c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
479c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // If we're using smart reply/forward, we need info about the original message
480c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (mSendTag != Tags.COMPOSE_SEND_MAIL) {
481c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                if (mSmartSendInfo != null) {
482c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    s.start(Tags.COMPOSE_SOURCE);
483c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    // For search results, use the long id (stored in mProtocolSearchInfo); else,
484c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    // use folder id/item id combo
485c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    if (mMessage.mProtocolSearchInfo != null) {
486c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        s.data(Tags.COMPOSE_LONG_ID, mMessage.mProtocolSearchInfo);
487c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    } else {
488c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        s.data(Tags.COMPOSE_ITEM_ID, mSmartSendInfo.mItemId);
489c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                        s.data(Tags.COMPOSE_FOLDER_ID, mSmartSendInfo.mCollectionId);
490c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    }
491c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                    s.end();  // Tags.COMPOSE_SOURCE
492c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                }
493c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
494c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu
495c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // Start the MIME tag; this is followed by "opaque" data (byte array)
496c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            s.start(Tags.COMPOSE_MIME);
497c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // Send opaque data from the file stream
498c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            if (withData) {
499c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                s.opaque(mFileStream, (int)mFileLength);
500c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            } else {
501c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu                s.opaqueWithoutData((int)mFileLength);
502c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            }
503c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            // And we're done
504c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu            s.end().end().done();
505c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu        }
506c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu    }
507c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu}
508