LegacyConversions.java revision 0d1078363581db8caded06cf94e729e88a88761a
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.email;
18
19import com.android.email.mail.Address;
20import com.android.email.mail.Message;
21import com.android.email.mail.MessagingException;
22import com.android.email.mail.Part;
23import com.android.email.mail.internet.MimeHeader;
24import com.android.email.mail.internet.MimeUtility;
25import com.android.email.provider.AttachmentProvider;
26import com.android.email.provider.EmailContent;
27import com.android.email.provider.EmailContent.Attachment;
28import com.android.email.provider.EmailContent.AttachmentColumns;
29
30import org.apache.commons.io.IOUtils;
31
32import android.content.ContentUris;
33import android.content.ContentValues;
34import android.content.Context;
35import android.net.Uri;
36
37import java.io.File;
38import java.io.FileOutputStream;
39import java.io.IOException;
40import java.io.InputStream;
41import java.util.ArrayList;
42import java.util.Date;
43
44public class LegacyConversions {
45
46    /**
47     * Copy field-by-field from a "store" message to a "provider" message
48     * @param message The message we've just downloaded
49     * @param localMessage The message we'd like to write into the DB
50     * @result true if dirty (changes were made)
51     */
52    public static boolean updateMessageFields(EmailContent.Message localMessage, Message message,
53                long accountId, long mailboxId) throws MessagingException {
54
55        Address[] from = message.getFrom();
56        Address[] to = message.getRecipients(Message.RecipientType.TO);
57        Address[] cc = message.getRecipients(Message.RecipientType.CC);
58        Address[] bcc = message.getRecipients(Message.RecipientType.BCC);
59        Address[] replyTo = message.getReplyTo();
60        String subject = message.getSubject();
61        Date sentDate = message.getSentDate();
62
63        if (from != null && from.length > 0) {
64            localMessage.mDisplayName = from[0].toFriendly();
65        }
66        if (sentDate != null) {
67            localMessage.mTimeStamp = sentDate.getTime();
68        }
69        if (subject != null) {
70            localMessage.mSubject = subject;
71        }
72//        public String mPreview;
73//        public boolean mFlagRead = false;
74
75        // Keep the message in the "unloaded" state until it has (at least) a display name.
76        // This prevents early flickering of empty messages in POP download.
77        if (localMessage.mFlagLoaded != EmailContent.Message.LOADED) {
78            if (localMessage.mDisplayName == null || "".equals(localMessage.mDisplayName)) {
79                localMessage.mFlagLoaded = EmailContent.Message.NOT_LOADED;
80            } else {
81                localMessage.mFlagLoaded = EmailContent.Message.PARTIALLY_LOADED;
82            }
83        }
84        // TODO handle flags, favorites, and read/unread
85//        public boolean mFlagFavorite = false;
86//        public boolean mFlagAttachment = false;
87//        public int mFlags = 0;
88//
89//        public String mTextInfo;
90//        public String mHtmlInfo;
91//
92        localMessage.mServerId = message.getUid();
93//        public int mServerIntId;
94//        public String mClientId;
95//        public String mMessageId;
96//        public String mThreadId;
97//
98//        public long mBodyKey;
99        localMessage.mMailboxKey = mailboxId;
100        localMessage.mAccountKey = accountId;
101//        public long mReferenceKey;
102//
103//        public String mSender;
104        if (from != null && from.length > 0) {
105            localMessage.mFrom = Address.pack(from);
106        }
107
108        localMessage.mTo = Address.pack(to);
109        localMessage.mCc = Address.pack(cc);
110        localMessage.mBcc = Address.pack(bcc);
111        localMessage.mReplyTo = Address.pack(replyTo);
112
113//        public String mServerVersion;
114//
115//        public String mText;
116//        public String mHtml;
117//
118//        // Can be used while building messages, but is NOT saved by the Provider
119//        transient public ArrayList<Attachment> mAttachments = null;
120
121        return true;
122    }
123
124    /**
125     * Copy body text (plain and/or HTML) from MimeMessage to provider Message
126     *
127     * TODO: Take a closer look at textInfo and deal with it if necessary.
128     */
129    public static boolean updateBodyFields(EmailContent.Body body,
130            EmailContent.Message localMessage, ArrayList<Part> viewables)
131            throws MessagingException {
132
133        body.mMessageKey = localMessage.mId;
134
135        StringBuffer sbHtml = new StringBuffer();
136        StringBuffer sbText = new StringBuffer();
137        for (Part viewable : viewables) {
138            String text = MimeUtility.getTextFromPart(viewable);
139            if ("text/html".equalsIgnoreCase(viewable.getMimeType())) {
140                if (sbHtml.length() > 0) {
141                    sbHtml.append('\n');
142                }
143                sbHtml.append(text);
144            } else {
145                if (sbText.length() > 0) {
146                    sbText.append('\n');
147                }
148                sbText.append(text);
149            }
150        }
151
152        // write the combined data to the body part
153        if (sbText.length() != 0) {
154            localMessage.mTextInfo = "X;X;8;" + sbText.length()*2;
155            body.mTextContent = sbText.toString();
156        }
157        if (sbHtml.length() != 0) {
158            localMessage.mHtmlInfo = "X;X;8;" + sbHtml.length()*2;
159            body.mHtmlContent = sbHtml.toString();
160        }
161        return true;
162    }
163
164    /**
165     * Copy attachments from MimeMessage to provider Message.
166     *
167     * @param context a context for file operations
168     * @param localMessage the attachments will be built against this message
169     * @param message the original message from POP or IMAP, that may have attachments
170     * @return true if it succeeded
171     * @throws IOException
172     */
173    public static void updateAttachments(Context context, EmailContent.Message localMessage,
174            ArrayList<Part> attachments) throws MessagingException, IOException {
175        localMessage.mAttachments = null;
176        for (Part attachmentPart : attachments) {
177            addOneAttachment(context, localMessage, attachmentPart);
178        }
179    }
180
181    /**
182     * Add a single attachment part to the message
183     *
184     * TODO: This will simply add to any existing attachments - could this ever happen?  If so,
185     * change it to find existing attachments and delete/merge them.
186     * TODO: Take a closer look at encoding and deal with it if necessary.
187     *
188     * @param context a context for file operations
189     * @param localMessage the attachments will be built against this message
190     * @param part a single attachment part from POP or IMAP
191     * @return true if it succeeded
192     * @throws IOException
193     */
194    private static void addOneAttachment(Context context, EmailContent.Message localMessage,
195            Part part) throws MessagingException, IOException {
196
197        Attachment localAttachment = new Attachment();
198
199        // Transfer fields from mime format to provider format
200        String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
201        String name = MimeUtility.getHeaderParameter(contentType, "name");
202        if (name == null) {
203            String contentDisposition = MimeUtility.unfoldAndDecode(part.getContentType());
204            name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
205        }
206
207        // Try to pull size from disposition (if not downloaded)
208        long size = 0;
209        String disposition = part.getDisposition();
210        if (disposition != null) {
211            String s = MimeUtility.getHeaderParameter(disposition, "size");
212            if (s != null) {
213                size = Long.parseLong(s);
214            }
215        }
216
217        // Get partId for unloaded IMAP attachments (if any)
218        // This is only provided (and used) when we have structure but not the actual attachment
219        String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA);
220        String partId = partIds != null ? partIds[0] : null;;
221
222        localAttachment.mFileName = MimeUtility.getHeaderParameter(contentType, "name");
223        localAttachment.mMimeType = part.getMimeType();
224        localAttachment.mSize = size;           // May be reset below if file handled
225        localAttachment.mContentId = part.getContentId();
226        localAttachment.mContentUri = null;     // Will be set when file is saved
227        localAttachment.mMessageKey = localMessage.mId;
228        localAttachment.mLocation = partId;
229        localAttachment.mEncoding = "B";        // TODO - convert other known encodings
230
231        // Save the attachment (so far) in order to obtain an id
232        localAttachment.save(context);
233
234        // If an attachment body was actually provided, we need to write the file now
235        saveAttachmentBody(context, part, localAttachment, localMessage.mAccountKey);
236
237        if (localMessage.mAttachments == null) {
238            localMessage.mAttachments = new ArrayList<Attachment>();
239        }
240        localMessage.mAttachments.add(localAttachment);
241        localMessage.mFlagAttachment = true;
242    }
243
244    /**
245     * Save the body part of a single attachment, to a file in the attachments directory.
246     */
247    public static void saveAttachmentBody(Context context, Part part, Attachment localAttachment,
248            long accountId) throws MessagingException, IOException {
249        if (part.getBody() != null) {
250            long attachmentId = localAttachment.mId;
251
252            InputStream in = part.getBody().getInputStream();
253
254            File saveIn = AttachmentProvider.getAttachmentDirectory(context, accountId);
255            if (!saveIn.exists()) {
256                saveIn.mkdirs();
257            }
258            File saveAs = AttachmentProvider.getAttachmentFilename(context, accountId,
259                    attachmentId);
260            saveAs.createNewFile();
261            FileOutputStream out = new FileOutputStream(saveAs);
262            long copySize = IOUtils.copy(in, out);
263            in.close();
264            out.close();
265
266            // update the attachment with the extra information we now know
267            String contentUriString = AttachmentProvider.getAttachmentUri(
268                    accountId, attachmentId).toString();
269
270            localAttachment.mSize = copySize;
271            localAttachment.mContentUri = contentUriString;
272
273            // update the attachment in the database as well
274            ContentValues cv = new ContentValues();
275            cv.put(AttachmentColumns.SIZE, copySize);
276            cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
277            Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, attachmentId);
278            context.getContentResolver().update(uri, cv, null, null);
279        }
280    }
281
282}
283