diff --git a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java index 8d0dd8f20..95c38f10e 100644 --- a/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -3194,7 +3194,7 @@ public class MessagingController implements Runnable { MimeMessageHelper.setBody(remoteMessage, message.getBody()); remoteFolder.fetchPart(remoteMessage, part, null); - localFolder.updateMessage((LocalMessage)message); + localFolder.addPartToMessage((LocalMessage) message, part); for (MessagingListener l : getListeners(listener)) { l.loadAttachmentFinished(account, message, part, tag); } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalFolder.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalFolder.java index 567de72d4..a4eb27112 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalFolder.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalFolder.java @@ -4,7 +4,6 @@ package com.fsck.k9.mailstore; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; @@ -19,20 +18,17 @@ import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.UUID; -import java.util.regex.Pattern; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; import android.util.Log; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.activity.Search; -import com.fsck.k9.helper.HtmlConverter; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Body; @@ -49,13 +45,10 @@ import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeMessage; -import com.fsck.k9.mail.internet.MimeMessageHelper; import com.fsck.k9.mail.internet.MimeMultipart; -import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.LockableDatabase.DbCallback; import com.fsck.k9.mailstore.LockableDatabase.WrappedException; import com.fsck.k9.provider.AttachmentProvider; -import org.apache.commons.io.IOUtils; import org.apache.james.mime4j.MimeException; import org.apache.james.mime4j.parser.ContentHandler; import org.apache.james.mime4j.parser.MimeStreamParser; @@ -1230,10 +1223,10 @@ public class LocalFolder extends Folder implements Serializable { if (oldMessage != null) { oldMessageId = oldMessage.getId(); - } - //FIXME - deleteAttachments(message.getUid()); + long oldRootMessagePartId = oldMessage.getMessagePartId(); + deleteMessagePartsAndDataFromDisk(oldRootMessagePartId); + } } long rootId = -1; @@ -1408,274 +1401,11 @@ public class LocalFolder extends Folder implements Serializable { } } - /** - * Update the given message in the LocalStore without first deleting the existing - * message (contrast with appendMessages). This method is used to store changes - * to the given message while updating attachments and not removing existing - * attachment data. - * TODO In the future this method should be combined with appendMessages since the Message - * contains enough data to decide what to do. - * @param message - * @throws MessagingException - */ - public void updateMessage(final LocalMessage message) throws MessagingException { + public void addPartToMessage(final LocalMessage message, final Part part) throws MessagingException { open(OPEN_MODE_RW); - try { - this.localStore.database.execute(false, new DbCallback() { - @Override - public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { - try { - ViewableContainer container = - LocalMessageExtractor.extractTextAndAttachments(LocalFolder.this.localStore.context, message); + throw new RuntimeException("Not implemented yet"); - List attachments = container.attachments; - String text = container.text; - String html = HtmlConverter.convertEmoji2Img(container.html); - - String preview = Message.calculateContentPreview(text); - - try { - db.execSQL("UPDATE messages SET " - + "uid = ?, subject = ?, sender_list = ?, date = ?, flags = ?, " - + "folder_id = ?, to_list = ?, cc_list = ?, bcc_list = ?, " - + "html_content = ?, text_content = ?, preview = ?, reply_to_list = ?, " - + "attachment_count = ?, read = ?, flagged = ?, answered = ?, forwarded = ? " - + "WHERE id = ?", - new Object[] { - message.getUid(), - message.getSubject(), - Address.pack(message.getFrom()), - message.getSentDate() == null ? System - .currentTimeMillis() : message.getSentDate() - .getTime(), - LocalFolder.this.localStore.serializeFlags(message.getFlags()), - mFolderId, - Address.pack(message - .getRecipients(RecipientType.TO)), - Address.pack(message - .getRecipients(RecipientType.CC)), - Address.pack(message - .getRecipients(RecipientType.BCC)), - html.length() > 0 ? html : null, - text.length() > 0 ? text : null, - preview.length() > 0 ? preview : null, - Address.pack(message.getReplyTo()), - attachments.size(), - message.isSet(Flag.SEEN) ? 1 : 0, - message.isSet(Flag.FLAGGED) ? 1 : 0, - message.isSet(Flag.ANSWERED) ? 1 : 0, - message.isSet(Flag.FORWARDED) ? 1 : 0, - message.getId() - }); - - for (int i = 0, count = attachments.size(); i < count; i++) { - Part attachment = attachments.get(i); - saveAttachment(message.getId(), attachment, false); - } - - //FIXME - //saveHeaders(message.getId(), message); - } catch (Exception e) { - throw new MessagingException("Error appending message", e); - } - } catch (MessagingException e) { - throw new WrappedException(e); - } - return null; - } - }); - } catch (WrappedException e) { - throw(MessagingException) e.getCause(); - } - - this.localStore.notifyChange(); - } - - /** - * @param messageId - * @param attachment - * @param saveAsNew - * @throws IOException - * @throws MessagingException - */ - private void saveAttachment(final long messageId, final Part attachment, final boolean saveAsNew) - throws IOException, MessagingException { - try { - this.localStore.database.execute(true, new DbCallback() { - @Override - public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { - try { - long attachmentId = -1; - Uri contentUri = null; - int size = -1; - File tempAttachmentFile = null; - - if ((!saveAsNew) && (attachment instanceof LocalAttachmentBodyPart)) { - attachmentId = ((LocalAttachmentBodyPart) attachment).getAttachmentId(); - } - - final File attachmentDirectory = StorageManager.getInstance(LocalFolder.this.localStore.context).getAttachmentDirectory(LocalFolder.this.localStore.uUid, LocalFolder.this.localStore.database.getStorageProviderId()); - if (attachment.getBody() != null) { - Body body = attachment.getBody(); - if (body instanceof LocalAttachmentBody) { - contentUri = ((LocalAttachmentBody) body).getContentUri(); - } else if (body instanceof Message) { - // It's a message, so use Message.writeTo() to output the - // message including all children. - Message message = (Message) body; - tempAttachmentFile = File.createTempFile("att", null, attachmentDirectory); - FileOutputStream out = new FileOutputStream(tempAttachmentFile); - try { - message.writeTo(out); - } finally { - out.close(); - } - size = (int) (tempAttachmentFile.length() & 0x7FFFFFFFL); - } else { - /* - * If the attachment has a body we're expected to save it into the local store - * so we copy the data into a cached attachment file. - */ - InputStream in = MimeUtility.decodeBody(attachment.getBody()); - try { - tempAttachmentFile = File.createTempFile("att", null, attachmentDirectory); - FileOutputStream out = new FileOutputStream(tempAttachmentFile); - try { - size = IOUtils.copy(in, out); - } finally { - out.close(); - } - } finally { - try { in.close(); } catch (Throwable ignore) {} - } - } - } - - if (size == -1) { - /* - * If the attachment is not yet downloaded see if we can pull a size - * off the Content-Disposition. - */ - String disposition = attachment.getDisposition(); - if (disposition != null) { - String sizeParam = MimeUtility.getHeaderParameter(disposition, "size"); - if (sizeParam != null) { - try { - size = Integer.parseInt(sizeParam); - } catch (NumberFormatException e) { /* Ignore */ } - } - } - } - if (size == -1) { - size = 0; - } - - String storeData = - Utility.combine(attachment.getHeader( - MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA), ','); - - String name = MimeUtility.getHeaderParameter(attachment.getContentType(), "name"); - String contentId = MimeUtility.getHeaderParameter(attachment.getContentId(), null); - - String contentDisposition = MimeUtility.unfoldAndDecode(attachment.getDisposition()); - String dispositionType = contentDisposition; - - if (dispositionType != null) { - int pos = dispositionType.indexOf(';'); - if (pos != -1) { - // extract the disposition-type, "attachment", "inline" or extension-token (see the RFC 2183) - dispositionType = dispositionType.substring(0, pos); - } - } - - if (name == null && contentDisposition != null) { - name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); - } - if (attachmentId == -1) { - ContentValues cv = new ContentValues(); - cv.put("message_id", messageId); - cv.put("content_uri", contentUri != null ? contentUri.toString() : null); - cv.put("store_data", storeData); - cv.put("size", size); - cv.put("name", name); - cv.put("mime_type", attachment.getMimeType()); - cv.put("content_id", contentId); - cv.put("content_disposition", dispositionType); - - attachmentId = db.insert("attachments", "message_id", cv); - } else { - ContentValues cv = new ContentValues(); - cv.put("content_uri", contentUri != null ? contentUri.toString() : null); - cv.put("size", size); - db.update("attachments", cv, "id = ?", new String[] - { Long.toString(attachmentId) }); - } - - if (attachmentId != -1 && tempAttachmentFile != null) { - File attachmentFile = new File(attachmentDirectory, Long.toString(attachmentId)); - tempAttachmentFile.renameTo(attachmentFile); - contentUri = AttachmentProvider.getAttachmentUri( - getAccount(), - attachmentId); - if (MimeUtil.isMessage(attachment.getMimeType())) { - LocalAttachmentMessageBody body = new LocalAttachmentMessageBody( - contentUri, LocalFolder.this.localStore.context); - MimeMessageHelper.setBody(attachment, body); - } else { - LocalAttachmentBody body = new LocalAttachmentBody( - contentUri, LocalFolder.this.localStore.context); - MimeMessageHelper.setBody(attachment, body); - } - ContentValues cv = new ContentValues(); - cv.put("content_uri", contentUri != null ? contentUri.toString() : null); - db.update("attachments", cv, "id = ?", new String[] - { Long.toString(attachmentId) }); - } - - /* The message has attachment with Content-ID */ - if (contentId != null && contentUri != null) { - Cursor cursor = db.query("messages", new String[] - { "html_content" }, "id = ?", new String[] - { Long.toString(messageId) }, null, null, null); - try { - if (cursor.moveToNext()) { - String htmlContent = cursor.getString(0); - - if (htmlContent != null) { - String newHtmlContent = htmlContent.replaceAll( - Pattern.quote("cid:" + contentId), - contentUri.toString()); - - ContentValues cv = new ContentValues(); - cv.put("html_content", newHtmlContent); - db.update("messages", cv, "id = ?", new String[] - { Long.toString(messageId) }); - } - } - } finally { - Utility.closeQuietly(cursor); - } - } - - if (attachmentId != -1 && attachment instanceof LocalAttachmentBodyPart) { - ((LocalAttachmentBodyPart) attachment).setAttachmentId(attachmentId); - } - return null; - } catch (MessagingException e) { - throw new WrappedException(e); - } catch (IOException e) { - throw new WrappedException(e); - } - } - }); - } catch (WrappedException e) { - final Throwable cause = e.getCause(); - if (cause instanceof IOException) { - throw (IOException) cause; - } - - throw (MessagingException) cause; - } +// localStore.notifyChange(); } /** @@ -1770,21 +1500,18 @@ public class LocalFolder extends Folder implements Serializable { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { try { - // Get UIDs for all messages to delete - Cursor cursor = db.query("messages", new String[] { "uid" }, + Cursor cursor = db.query("messages", new String[] { "message_part_id" }, "folder_id = ? AND (empty IS NULL OR empty != 1)", folderIdArg, null, null, null); - try { - // Delete attachments of these messages while (cursor.moveToNext()) { - deleteAttachments(cursor.getString(0)); + long messagePartId = cursor.getLong(0); + deleteMessageDataFromDisk(messagePartId); } } finally { cursor.close(); } - // Delete entries in 'threads' and 'messages' db.execSQL("DELETE FROM threads WHERE message_id IN " + "(SELECT id FROM messages WHERE folder_id = ?)", folderIdArg); db.execSQL("DELETE FROM messages WHERE folder_id = ?", folderIdArg); @@ -1816,9 +1543,9 @@ public class LocalFolder extends Folder implements Serializable { try { // We need to open the folder first to make sure we've got it's id open(OPEN_MODE_RO); - List messages = getMessages(null); - for (Message message : messages) { - deleteAttachments(message.getUid()); + List messages = getMessages(null); + for (LocalMessage message : messages) { + deleteMessageDataFromDisk(message.getMessagePartId()); } } catch (MessagingException e) { throw new WrappedException(e); @@ -1846,75 +1573,68 @@ public class LocalFolder extends Folder implements Serializable { return mName.hashCode(); } - void deleteAttachments(final long messageId) throws MessagingException { - open(OPEN_MODE_RW); - this.localStore.database.execute(false, new DbCallback() { + void deleteMessagePartsAndDataFromDisk(final long rootMessagePartId) throws MessagingException { + deleteMessageDataFromDisk(rootMessagePartId); + deleteMessageParts(rootMessagePartId); + } + + private void deleteMessageParts(final long rootMessagePartId) throws MessagingException { + localStore.database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { - Cursor attachmentsCursor = null; - try { - String accountUuid = getAccountUuid(); - Context context = LocalFolder.this.localStore.context; - - // Get attachment IDs - String[] whereArgs = new String[] { Long.toString(messageId) }; - attachmentsCursor = db.query("attachments", new String[] { "id" }, - "message_id = ?", whereArgs, null, null, null); - - final File attachmentDirectory = StorageManager.getInstance(LocalFolder.this.localStore.context) - .getAttachmentDirectory(LocalFolder.this.localStore.uUid, LocalFolder.this.localStore.database.getStorageProviderId()); - - while (attachmentsCursor.moveToNext()) { - String attachmentId = Long.toString(attachmentsCursor.getLong(0)); - try { - // Delete stored attachment - File file = new File(attachmentDirectory, attachmentId); - if (file.exists()) { - file.delete(); - } - - // Delete thumbnail file - AttachmentProvider.deleteThumbnail(context, accountUuid, - attachmentId); - } catch (Exception e) { /* ignore */ } - } - - // Delete attachment metadata from the database - db.delete("attachments", "message_id = ?", whereArgs); - } finally { - Utility.closeQuietly(attachmentsCursor); - } + db.delete("message_parts", "root = ?", new String[] { Long.toString(rootMessagePartId) }); return null; } }); } - private void deleteAttachments(final String uid) throws MessagingException { - open(OPEN_MODE_RW); - try { - this.localStore.database.execute(false, new DbCallback() { - @Override - public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { - Cursor messagesCursor = null; - try { - messagesCursor = db.query("messages", new String[] - { "id" }, "folder_id = ? AND uid = ?", new String[] - { Long.toString(mFolderId), uid }, null, null, null); - while (messagesCursor.moveToNext()) { - long messageId = messagesCursor.getLong(0); - deleteAttachments(messageId); + private void deleteMessageDataFromDisk(final long rootMessagePartId) throws MessagingException { + localStore.database.execute(false, new DbCallback() { + @Override + public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { + deleteMessagePartsFromDisk(db, rootMessagePartId); + deleteAttachmentThumbnailsFromDisk(db, rootMessagePartId); + return null; + } + }); + } - } - } catch (MessagingException e) { - throw new WrappedException(e); - } finally { - Utility.closeQuietly(messagesCursor); + private void deleteMessagePartsFromDisk(SQLiteDatabase db, long rootMessagePartId) { + File attachmentDirectory = StorageManager.getInstance(localStore.context) + .getAttachmentDirectory(getAccountUuid(), localStore.database.getStorageProviderId()); + + Cursor cursor = db.query("message_parts", new String[] { "id" }, + "root = ? AND data_location = " + DataLocation.ON_DISK, + new String[] { Long.toString(rootMessagePartId) }, null, null, null); + try { + while (cursor.moveToNext()) { + String messagePartId = cursor.getString(0); + File file = new File(attachmentDirectory, messagePartId); + if (file.exists()) { + if (!file.delete() && K9.DEBUG) { + Log.d(K9.LOG_TAG, "Couldn't delete message part file: " + file.getAbsolutePath()); } - return null; } - }); - } catch (WrappedException e) { - throw(MessagingException) e.getCause(); + } + } finally { + cursor.close(); + } + } + + private void deleteAttachmentThumbnailsFromDisk(SQLiteDatabase db, long rootMessagePartId) { + Context context = localStore.context; + String accountUuid = getAccountUuid(); + + Cursor cursor = db.query("message_parts", new String[] { "id" }, + "root = ? AND type = " + MessagePartType.ATTACHMENT, + new String[] { Long.toString(rootMessagePartId) }, null, null, null); + try { + while (cursor.moveToNext()) { + String messagePartId = cursor.getString(0); + AttachmentProvider.deleteThumbnail(context, accountUuid, messagePartId); + } + } finally { + cursor.close(); } } @@ -2185,7 +1905,7 @@ public class LocalFolder extends Folder implements Serializable { } // Note: The contents of the 'message_parts' table depend on these values. - private static class DataLocation { + static class DataLocation { static final int MISSING = 0; static final int IN_DATABASE = 1; static final int ON_DISK = 2; diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessage.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessage.java index 9c3be5752..7a0fa44cb 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessage.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessage.java @@ -290,23 +290,14 @@ public class LocalMessage extends MimeMessage { } /* - * If a message is being marked as deleted we want to clear out it's content - * and attachments as well. Delete will not actually remove the row since we need - * to retain the uid for synchronization purposes. + * If a message is being marked as deleted we want to clear out its content. Delete will not actually remove the + * row since we need to retain the UID for synchronization purposes. */ - private void delete() throws MessagingException - - { - /* - * Delete all of the message's content to save space. - */ + private void delete() throws MessagingException { try { - this.localStore.database.execute(true, new DbCallback() { + localStore.database.execute(true, new DbCallback() { @Override - public Void doDbWork(final SQLiteDatabase db) throws WrappedException, - UnavailableStorageException { - String[] idArg = new String[] { Long.toString(mId) }; - + public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { ContentValues cv = new ContentValues(); cv.put("deleted", 1); cv.put("empty", 1); @@ -320,29 +311,24 @@ public class LocalMessage extends MimeMessage { cv.putNull("html_content"); cv.putNull("text_content"); cv.putNull("reply_to_list"); + cv.putNull("message_part_id"); - db.update("messages", cv, "id = ?", idArg); + db.update("messages", cv, "id = ?", new String[] { Long.toString(mId) }); - /* - * Delete all of the message's attachments to save space. - * We do this explicit deletion here because we're not deleting the record - * in messages, which means our ON DELETE trigger for messages won't cascade - */ try { - ((LocalFolder) mFolder).deleteAttachments(mId); + ((LocalFolder) mFolder).deleteMessagePartsAndDataFromDisk(messagePartId); } catch (MessagingException e) { throw new WrappedException(e); } - db.delete("attachments", "message_id = ?", idArg); return null; } }); } catch (WrappedException e) { - throw(MessagingException) e.getCause(); + throw (MessagingException) e.getCause(); } - this.localStore.notifyChange(); + localStore.notifyChange(); } /* @@ -360,7 +346,7 @@ public class LocalMessage extends MimeMessage { try { LocalFolder localFolder = (LocalFolder) mFolder; - localFolder.deleteAttachments(mId); + localFolder.deleteMessagePartsAndDataFromDisk(messagePartId); if (hasThreadChildren(db, mId)) { // This message has children in the thread structure so we need to diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java index 2be6d0bac..b12e0a936 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalStore.java @@ -21,8 +21,8 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.MessageRetrievalListener; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Store; +import com.fsck.k9.mailstore.LocalFolder.DataLocation; import com.fsck.k9.mailstore.StorageManager.StorageProvider; -import com.fsck.k9.mail.store.StoreConfig; import com.fsck.k9.mailstore.LockableDatabase.DbCallback; import com.fsck.k9.mailstore.LockableDatabase.WrappedException; import com.fsck.k9.provider.EmailProvider; @@ -276,7 +276,7 @@ public class LocalStore extends Store implements Serializable { if (K9.DEBUG) Log.i(K9.LOG_TAG, "Before prune size = " + getSize()); - pruneCachedAttachments(true); + deleteAllMessageDataFromDisk(); if (K9.DEBUG) { Log.i(K9.LOG_TAG, "After prune / before compaction size = " + getSize()); @@ -398,73 +398,39 @@ public class LocalStore extends Store implements Serializable { database.recreate(); } - /** - * Deletes all cached attachments for the entire store. - * @param force - * @throws com.fsck.k9.mail.MessagingException - */ - //TODO this method seems to be only called with force=true, simplify accordingly - private void pruneCachedAttachments(final boolean force) throws MessagingException { + private void deleteAllMessageDataFromDisk() throws MessagingException { + markAllMessagePartsDataAsMissing(); + deleteAllMessagePartsDataFromDisk(); + } + + private void markAllMessagePartsDataAsMissing() throws MessagingException { database.execute(false, new DbCallback() { @Override public Void doDbWork(final SQLiteDatabase db) throws WrappedException { - if (force) { - ContentValues cv = new ContentValues(); - cv.putNull("content_uri"); - db.update("attachments", cv, null, null); - } - final StorageManager storageManager = StorageManager.getInstance(context); - File[] files = storageManager.getAttachmentDirectory(uUid, database.getStorageProviderId()).listFiles(); - for (File file : files) { - if (file.exists()) { - if (!force) { - Cursor cursor = null; - try { - cursor = db.query( - "attachments", - new String[] { "store_data" }, - "id = ?", - new String[] { file.getName() }, - null, - null, - null); - if (cursor.moveToNext()) { - if (cursor.getString(0) == null) { - if (K9.DEBUG) - Log.d(K9.LOG_TAG, "Attachment " + file.getAbsolutePath() + " has no store data, not deleting"); - /* - * If the attachment has no store data it is not recoverable, so - * we won't delete it. - */ - continue; - } - } - } finally { - Utility.closeQuietly(cursor); - } - } - if (!force) { - try { - ContentValues cv = new ContentValues(); - cv.putNull("content_uri"); - db.update("attachments", cv, "id = ?", new String[] { file.getName() }); - } catch (Exception e) { - /* - * If the row has gone away before we got to mark it not-downloaded that's - * okay. - */ - } - } - if (!file.delete()) { - file.deleteOnExit(); - } - } - } + ContentValues cv = new ContentValues(); + cv.put("data_location", DataLocation.MISSING); + db.update("message_parts", cv, null, null); + return null; } }); } + private void deleteAllMessagePartsDataFromDisk() { + final StorageManager storageManager = StorageManager.getInstance(context); + File attachmentDirectory = storageManager.getAttachmentDirectory(uUid, database.getStorageProviderId()); + File[] files = attachmentDirectory.listFiles(); + if (files == null) { + return; + } + + for (File file : files) { + if (file.exists() && !file.delete()) { + file.deleteOnExit(); + } + } + } + public void resetVisibleLimits(int visibleLimit) throws MessagingException { final ContentValues cv = new ContentValues(); cv.put("visible_limit", Integer.toString(visibleLimit)); @@ -672,32 +638,27 @@ public class LocalStore extends Store implements Serializable { return database.execute(false, new DbCallback() { @Override public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException { - String name; - String type; - int size; - Cursor cursor = null; + Cursor cursor = db.query("message_parts", + new String[] { "display_name", "decoded_body_size", "mime_type" }, + "id = ?", + new String[] { attachmentId }, + null, null, null); try { - cursor = db.query( - "attachments", - new String[] { "name", "size", "mime_type" }, - "id = ?", - new String[] { attachmentId }, - null, - null, - null); if (!cursor.moveToFirst()) { return null; } - name = cursor.getString(0); - size = cursor.getInt(1); - type = cursor.getString(2); + String name = cursor.getString(0); + int size = cursor.getInt(1); + String mimeType = cursor.getString(2); + final AttachmentInfo attachmentInfo = new AttachmentInfo(); attachmentInfo.name = name; attachmentInfo.size = size; - attachmentInfo.type = type; + attachmentInfo.type = mimeType; + return attachmentInfo; } finally { - Utility.closeQuietly(cursor); + cursor.close(); } } }); @@ -705,7 +666,7 @@ public class LocalStore extends Store implements Serializable { public static class AttachmentInfo { public String name; - public int size; + public int size; //FIXME: use long public String type; } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java b/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java index 2b727dc82..d8be088ee 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/StoreSchemaDefinition.java @@ -167,11 +167,6 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition { "UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " + "END"); - db.execSQL("DROP TABLE IF EXISTS attachments"); - db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER," - + "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT," - + "mime_type TEXT, content_id TEXT, content_disposition TEXT)"); - db.execSQL("DROP TABLE IF EXISTS pending_commands"); db.execSQL("CREATE TABLE pending_commands " + "(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)"); @@ -180,7 +175,11 @@ class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition { db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;"); db.execSQL("DROP TRIGGER IF EXISTS delete_message"); - db.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; END;"); + db.execSQL("CREATE TRIGGER delete_message " + + "BEFORE DELETE ON messages " + + "BEGIN " + + "DELETE FROM message_parts WHERE root = OLD.message_part_id;" + + "END"); } else { // in the case that we're starting out at 29 or newer, run all the needed updates