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