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