Home | History | Annotate | Download | only in service
      1 /* Copyright (C) 2012 The Android Open Source Project
      2  *
      3  * Licensed under the Apache License, Version 2.0 (the "License");
      4  * you may not use this file except in compliance with the License.
      5  * You may obtain a copy of the License at
      6  *
      7  *      http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software
     10  * distributed under the License is distributed on an "AS IS" BASIS,
     11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12  * See the License for the specific language governing permissions and
     13  * limitations under the License.
     14  */
     15 
     16 package com.android.email.service;
     17 
     18 import android.content.ContentResolver;
     19 import android.content.ContentUris;
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.database.Cursor;
     23 import android.net.TrafficStats;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.RemoteException;
     27 import android.text.TextUtils;
     28 
     29 import com.android.email.NotificationController;
     30 import com.android.email.mail.Sender;
     31 import com.android.email.mail.Store;
     32 import com.android.email.provider.AccountReconciler;
     33 import com.android.email.provider.Utilities;
     34 import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
     35 import com.android.email2.ui.MailActivityEmail;
     36 import com.android.emailcommon.Api;
     37 import com.android.emailcommon.Logging;
     38 import com.android.emailcommon.TrafficFlags;
     39 import com.android.emailcommon.internet.MimeBodyPart;
     40 import com.android.emailcommon.internet.MimeHeader;
     41 import com.android.emailcommon.internet.MimeMultipart;
     42 import com.android.emailcommon.mail.AuthenticationFailedException;
     43 import com.android.emailcommon.mail.FetchProfile;
     44 import com.android.emailcommon.mail.Folder;
     45 import com.android.emailcommon.mail.Folder.MessageRetrievalListener;
     46 import com.android.emailcommon.mail.Folder.OpenMode;
     47 import com.android.emailcommon.mail.Message;
     48 import com.android.emailcommon.mail.MessagingException;
     49 import com.android.emailcommon.provider.Account;
     50 import com.android.emailcommon.provider.EmailContent;
     51 import com.android.emailcommon.provider.EmailContent.Attachment;
     52 import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
     53 import com.android.emailcommon.provider.EmailContent.Body;
     54 import com.android.emailcommon.provider.EmailContent.BodyColumns;
     55 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     56 import com.android.emailcommon.provider.EmailContent.MessageColumns;
     57 import com.android.emailcommon.provider.HostAuth;
     58 import com.android.emailcommon.provider.Mailbox;
     59 import com.android.emailcommon.service.EmailServiceStatus;
     60 import com.android.emailcommon.service.IEmailService;
     61 import com.android.emailcommon.service.IEmailServiceCallback;
     62 import com.android.emailcommon.service.SearchParams;
     63 import com.android.emailcommon.utility.AttachmentUtilities;
     64 import com.android.emailcommon.utility.Utility;
     65 import com.android.mail.providers.UIProvider;
     66 import com.android.mail.providers.UIProvider.DraftType;
     67 import com.android.mail.utils.LogUtils;
     68 
     69 import java.util.HashSet;
     70 
     71 /**
     72  * EmailServiceStub is an abstract class representing an EmailService
     73  *
     74  * This class provides legacy support for a few methods that are common to both
     75  * IMAP and POP3, including startSync, loadMore, loadAttachment, and sendMail
     76  */
     77 public abstract class EmailServiceStub extends IEmailService.Stub implements IEmailService {
     78 
     79     private static final int MAILBOX_COLUMN_ID = 0;
     80     private static final int MAILBOX_COLUMN_SERVER_ID = 1;
     81     private static final int MAILBOX_COLUMN_TYPE = 2;
     82 
     83     /** Small projection for just the columns required for a sync. */
     84     private static final String[] MAILBOX_PROJECTION = new String[] {
     85         MailboxColumns.ID,
     86         MailboxColumns.SERVER_ID,
     87         MailboxColumns.TYPE,
     88     };
     89 
     90     protected Context mContext;
     91 
     92     protected void init(Context context) {
     93         mContext = context;
     94     }
     95 
     96     @Override
     97     public Bundle validate(HostAuth hostauth) throws RemoteException {
     98         // TODO Auto-generated method stub
     99         return null;
    100     }
    101 
    102     @Deprecated
    103     @Override
    104     public void startSync(long mailboxId, boolean userRequest, int deltaMessageCount)
    105             throws RemoteException {
    106         final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
    107         if (mailbox == null) return;
    108         final Account account = Account.restoreAccountWithId(mContext, mailbox.mAccountKey);
    109         if (account == null) return;
    110         final EmailServiceInfo info =
    111                 EmailServiceUtils.getServiceInfoForAccount(mContext, account.mId);
    112         final android.accounts.Account acct = new android.accounts.Account(account.mEmailAddress,
    113                 info.accountType);
    114         final Bundle extras = Mailbox.createSyncBundle(mailboxId);
    115         if (userRequest) {
    116             extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    117             extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
    118             extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    119         }
    120         if (deltaMessageCount != 0) {
    121             extras.putInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, deltaMessageCount);
    122         }
    123         ContentResolver.requestSync(acct, EmailContent.AUTHORITY, extras);
    124         LogUtils.i(Logging.LOG_TAG, "requestSync EmailServiceStub startSync %s, %s",
    125                 account.toString(), extras.toString());
    126     }
    127 
    128     @Override
    129     public void stopSync(long mailboxId) throws RemoteException {
    130         // Not required
    131     }
    132 
    133     @Override
    134     public void loadMore(long messageId) throws RemoteException {
    135         // Load a message for view...
    136         try {
    137             // 1. Resample the message, in case it disappeared or synced while
    138             // this command was in queue
    139             final EmailContent.Message message =
    140                 EmailContent.Message.restoreMessageWithId(mContext, messageId);
    141             if (message == null) {
    142                 return;
    143             }
    144             if (message.mFlagLoaded == EmailContent.Message.FLAG_LOADED_COMPLETE) {
    145                 // We should NEVER get here
    146                 return;
    147             }
    148 
    149             // 2. Open the remote folder.
    150             // TODO combine with common code in loadAttachment
    151             final Account account = Account.restoreAccountWithId(mContext, message.mAccountKey);
    152             final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);
    153             if (account == null || mailbox == null) {
    154                 //mListeners.loadMessageForViewFailed(messageId, "null account or mailbox");
    155                 return;
    156             }
    157             TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, account));
    158 
    159             final Store remoteStore = Store.getInstance(account, mContext);
    160             final String remoteServerId;
    161             // If this is a search result, use the protocolSearchInfo field to get the
    162             // correct remote location
    163             if (!TextUtils.isEmpty(message.mProtocolSearchInfo)) {
    164                 remoteServerId = message.mProtocolSearchInfo;
    165             } else {
    166                 remoteServerId = mailbox.mServerId;
    167             }
    168             final Folder remoteFolder = remoteStore.getFolder(remoteServerId);
    169             remoteFolder.open(OpenMode.READ_WRITE);
    170 
    171             // 3. Set up to download the entire message
    172             final Message remoteMessage = remoteFolder.getMessage(message.mServerId);
    173             final FetchProfile fp = new FetchProfile();
    174             fp.add(FetchProfile.Item.BODY);
    175             remoteFolder.fetch(new Message[] { remoteMessage }, fp, null);
    176 
    177             // 4. Write to provider
    178             Utilities.copyOneMessageToProvider(mContext, remoteMessage, account, mailbox,
    179                     EmailContent.Message.FLAG_LOADED_COMPLETE);
    180         } catch (MessagingException me) {
    181             if (Logging.LOGD) LogUtils.v(Logging.LOG_TAG, "", me);
    182 
    183         } catch (RuntimeException rte) {
    184             LogUtils.d(Logging.LOG_TAG, "RTE During loadMore");
    185         }
    186     }
    187 
    188     @Override
    189     public void loadAttachment(final IEmailServiceCallback cb, final long attachmentId,
    190             final boolean background) throws RemoteException {
    191         Folder remoteFolder = null;
    192         try {
    193             //1. Check if the attachment is already here and return early in that case
    194             Attachment attachment =
    195                 Attachment.restoreAttachmentWithId(mContext, attachmentId);
    196             if (attachment == null) {
    197                 cb.loadAttachmentStatus(0, attachmentId,
    198                         EmailServiceStatus.ATTACHMENT_NOT_FOUND, 0);
    199                 return;
    200             }
    201             final long messageId = attachment.mMessageKey;
    202 
    203             final EmailContent.Message message =
    204                     EmailContent.Message.restoreMessageWithId(mContext, attachment.mMessageKey);
    205             if (message == null) {
    206                 cb.loadAttachmentStatus(messageId, attachmentId,
    207                         EmailServiceStatus.MESSAGE_NOT_FOUND, 0);
    208                 return;
    209             }
    210 
    211             // If the message is loaded, just report that we're finished
    212             if (Utility.attachmentExists(mContext, attachment)
    213                     && attachment.mUiState == UIProvider.AttachmentState.SAVED) {
    214                 cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS,
    215                         0);
    216                 return;
    217             }
    218 
    219             // Say we're starting...
    220             cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.IN_PROGRESS, 0);
    221 
    222             // 2. Open the remote folder.
    223             final Account account = Account.restoreAccountWithId(mContext, message.mAccountKey);
    224             Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMailboxKey);
    225 
    226             if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
    227                 long sourceId = Utility.getFirstRowLong(mContext, Body.CONTENT_URI,
    228                         new String[] {BodyColumns.SOURCE_MESSAGE_KEY},
    229                         BodyColumns.MESSAGE_KEY + "=?",
    230                         new String[] {Long.toString(messageId)}, null, 0, -1L);
    231                 if (sourceId != -1) {
    232                     EmailContent.Message sourceMsg =
    233                             EmailContent.Message.restoreMessageWithId(mContext, sourceId);
    234                     if (sourceMsg != null) {
    235                         mailbox = Mailbox.restoreMailboxWithId(mContext, sourceMsg.mMailboxKey);
    236                         message.mServerId = sourceMsg.mServerId;
    237                     }
    238                 }
    239             } else if (mailbox.mType == Mailbox.TYPE_SEARCH && message.mMainMailboxKey != 0) {
    240                 mailbox = Mailbox.restoreMailboxWithId(mContext, message.mMainMailboxKey);
    241             }
    242 
    243             if (account == null || mailbox == null) {
    244                 // If the account/mailbox are gone, just report success; the UI handles this
    245                 cb.loadAttachmentStatus(messageId, attachmentId,
    246                         EmailServiceStatus.SUCCESS, 0);
    247                 return;
    248             }
    249             TrafficStats.setThreadStatsTag(
    250                     TrafficFlags.getAttachmentFlags(mContext, account));
    251 
    252             final Store remoteStore = Store.getInstance(account, mContext);
    253             remoteFolder = remoteStore.getFolder(mailbox.mServerId);
    254             remoteFolder.open(OpenMode.READ_WRITE);
    255 
    256             // 3. Generate a shell message in which to retrieve the attachment,
    257             // and a shell BodyPart for the attachment.  Then glue them together.
    258             final Message storeMessage = remoteFolder.createMessage(message.mServerId);
    259             final MimeBodyPart storePart = new MimeBodyPart();
    260             storePart.setSize((int)attachment.mSize);
    261             storePart.setHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA,
    262                     attachment.mLocation);
    263             storePart.setHeader(MimeHeader.HEADER_CONTENT_TYPE,
    264                     String.format("%s;\n name=\"%s\"",
    265                     attachment.mMimeType,
    266                     attachment.mFileName));
    267 
    268             // TODO is this always true for attachments?  I think we dropped the
    269             // true encoding along the way
    270             storePart.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
    271 
    272             final MimeMultipart multipart = new MimeMultipart();
    273             multipart.setSubType("mixed");
    274             multipart.addBodyPart(storePart);
    275 
    276             storeMessage.setHeader(MimeHeader.HEADER_CONTENT_TYPE, "multipart/mixed");
    277             storeMessage.setBody(multipart);
    278 
    279             // 4. Now ask for the attachment to be fetched
    280             final FetchProfile fp = new FetchProfile();
    281             fp.add(storePart);
    282             remoteFolder.fetch(new Message[] { storeMessage }, fp,
    283                     new MessageRetrievalListenerBridge(messageId, attachmentId, cb));
    284 
    285             // If we failed to load the attachment, throw an Exception here, so that
    286             // AttachmentDownloadService knows that we failed
    287             if (storePart.getBody() == null) {
    288                 throw new MessagingException("Attachment not loaded.");
    289             }
    290 
    291             // Save the attachment to wherever it's going
    292             AttachmentUtilities.saveAttachment(mContext, storePart.getBody().getInputStream(),
    293                     attachment);
    294 
    295             // 6. Report success
    296             cb.loadAttachmentStatus(messageId, attachmentId, EmailServiceStatus.SUCCESS, 0);
    297 
    298         } catch (MessagingException me) {
    299             LogUtils.i(Logging.LOG_TAG, me, "Error loading attachment");
    300 
    301             final ContentValues cv = new ContentValues(1);
    302             cv.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.FAILED);
    303             final Uri uri = ContentUris.withAppendedId(Attachment.CONTENT_URI, attachmentId);
    304             mContext.getContentResolver().update(uri, cv, null, null);
    305 
    306             cb.loadAttachmentStatus(0, attachmentId, EmailServiceStatus.CONNECTION_ERROR, 0);
    307         } finally {
    308             if (remoteFolder != null) {
    309                 remoteFolder.close(false);
    310             }
    311         }
    312 
    313     }
    314 
    315     /**
    316      * Bridge to intercept {@link MessageRetrievalListener#loadAttachmentProgress} and
    317      * pass down to {@link IEmailServiceCallback}.
    318      */
    319     public class MessageRetrievalListenerBridge implements MessageRetrievalListener {
    320         private final long mMessageId;
    321         private final long mAttachmentId;
    322         private final IEmailServiceCallback mCallback;
    323 
    324 
    325         public MessageRetrievalListenerBridge(final long messageId, final long attachmentId,
    326                 final IEmailServiceCallback callback) {
    327             mMessageId = messageId;
    328             mAttachmentId = attachmentId;
    329             mCallback = callback;
    330         }
    331 
    332         @Override
    333         public void loadAttachmentProgress(int progress) {
    334             try {
    335                 mCallback.loadAttachmentStatus(mMessageId, mAttachmentId,
    336                         EmailServiceStatus.IN_PROGRESS, progress);
    337             } catch (final RemoteException e) {
    338                 // No danger if the client is no longer around
    339             }
    340         }
    341 
    342         @Override
    343         public void messageRetrieved(com.android.emailcommon.mail.Message message) {
    344         }
    345     }
    346 
    347     @Override
    348     public void updateFolderList(long accountId) throws RemoteException {
    349         final Account account = Account.restoreAccountWithId(mContext, accountId);
    350         if (account == null) return;
    351         long inboxId = -1;
    352         TrafficStats.setThreadStatsTag(TrafficFlags.getSyncFlags(mContext, account));
    353         Cursor localFolderCursor = null;
    354         try {
    355             // Step 0: Make sure the default system mailboxes exist.
    356             for (final int type : Mailbox.REQUIRED_FOLDER_TYPES) {
    357                 if (Mailbox.findMailboxOfType(mContext, accountId, type) == Mailbox.NO_MAILBOX) {
    358                     final Mailbox mailbox = Mailbox.newSystemMailbox(mContext, accountId, type);
    359                     mailbox.save(mContext);
    360                     if (type == Mailbox.TYPE_INBOX) {
    361                         inboxId = mailbox.mId;
    362                     }
    363                 }
    364             }
    365 
    366             // Step 1: Get remote mailboxes
    367             final Store store = Store.getInstance(account, mContext);
    368             final Folder[] remoteFolders = store.updateFolders();
    369             final HashSet<String> remoteFolderNames = new HashSet<String>();
    370             for (final Folder remoteFolder : remoteFolders) {
    371                 remoteFolderNames.add(remoteFolder.getName());
    372             }
    373 
    374             // Step 2: Get local mailboxes
    375             localFolderCursor = mContext.getContentResolver().query(
    376                     Mailbox.CONTENT_URI,
    377                     MAILBOX_PROJECTION,
    378                     EmailContent.MailboxColumns.ACCOUNT_KEY + "=?",
    379                     new String[] { String.valueOf(account.mId) },
    380                     null);
    381 
    382             // Step 3: Remove any local mailbox not on the remote list
    383             while (localFolderCursor.moveToNext()) {
    384                 final String mailboxPath = localFolderCursor.getString(MAILBOX_COLUMN_SERVER_ID);
    385                 // Short circuit if we have a remote mailbox with the same name
    386                 if (remoteFolderNames.contains(mailboxPath)) {
    387                     continue;
    388                 }
    389 
    390                 final int mailboxType = localFolderCursor.getInt(MAILBOX_COLUMN_TYPE);
    391                 final long mailboxId = localFolderCursor.getLong(MAILBOX_COLUMN_ID);
    392                 switch (mailboxType) {
    393                     case Mailbox.TYPE_INBOX:
    394                     case Mailbox.TYPE_DRAFTS:
    395                     case Mailbox.TYPE_OUTBOX:
    396                     case Mailbox.TYPE_SENT:
    397                     case Mailbox.TYPE_TRASH:
    398                     case Mailbox.TYPE_SEARCH:
    399                         // Never, ever delete special mailboxes
    400                         break;
    401                     default:
    402                         // Drop all attachment files related to this mailbox
    403                         AttachmentUtilities.deleteAllMailboxAttachmentFiles(
    404                                 mContext, accountId, mailboxId);
    405                         // Delete the mailbox; database triggers take care of related
    406                         // Message, Body and Attachment records
    407                         Uri uri = ContentUris.withAppendedId(
    408                                 Mailbox.CONTENT_URI, mailboxId);
    409                         mContext.getContentResolver().delete(uri, null, null);
    410                         break;
    411                 }
    412             }
    413         } catch (MessagingException me) {
    414             LogUtils.i(Logging.LOG_TAG, me, "Error in updateFolderList");
    415             // We'll hope this is temporary
    416         } finally {
    417             if (localFolderCursor != null) {
    418                 localFolderCursor.close();
    419             }
    420             // If we just created the inbox, sync it
    421             if (inboxId != -1) {
    422                 startSync(inboxId, true, 0);
    423             }
    424         }
    425     }
    426 
    427     @Override
    428     public boolean createFolder(long accountId, String name) throws RemoteException {
    429         // Not required
    430         return false;
    431     }
    432 
    433     @Override
    434     public boolean deleteFolder(long accountId, String name) throws RemoteException {
    435         // Not required
    436         return false;
    437     }
    438 
    439     @Override
    440     public boolean renameFolder(long accountId, String oldName, String newName)
    441             throws RemoteException {
    442         // Not required
    443         return false;
    444     }
    445 
    446     @Override
    447     public void setLogging(int on) throws RemoteException {
    448         // Not required
    449     }
    450 
    451     @Override
    452     public void hostChanged(long accountId) throws RemoteException {
    453         // Not required
    454     }
    455 
    456     @Override
    457     public Bundle autoDiscover(String userName, String password) throws RemoteException {
    458         // Not required
    459        return null;
    460     }
    461 
    462     @Override
    463     public void sendMeetingResponse(long messageId, int response) throws RemoteException {
    464         // Not required
    465     }
    466 
    467     @Override
    468     public void deleteAccountPIMData(final String emailAddress) throws RemoteException {
    469         AccountReconciler.reconcileAccounts(mContext);
    470     }
    471 
    472     @Override
    473     public int getApiLevel() throws RemoteException {
    474         return Api.LEVEL;
    475     }
    476 
    477     @Override
    478     public int searchMessages(long accountId, SearchParams params, long destMailboxId)
    479             throws RemoteException {
    480         // Not required
    481         return 0;
    482     }
    483 
    484     @Override
    485     public void sendMail(long accountId) throws RemoteException {
    486         sendMailImpl(mContext, accountId);
    487     }
    488 
    489     public static void sendMailImpl(Context context, long accountId) {
    490         final Account account = Account.restoreAccountWithId(context, accountId);
    491         TrafficStats.setThreadStatsTag(TrafficFlags.getSmtpFlags(context, account));
    492         final NotificationController nc = NotificationController.getInstance(context);
    493         // 1.  Loop through all messages in the account's outbox
    494         final long outboxId = Mailbox.findMailboxOfType(context, account.mId, Mailbox.TYPE_OUTBOX);
    495         if (outboxId == Mailbox.NO_MAILBOX) {
    496             return;
    497         }
    498         final ContentResolver resolver = context.getContentResolver();
    499         final Cursor c = resolver.query(EmailContent.Message.CONTENT_URI,
    500                 EmailContent.Message.ID_COLUMN_PROJECTION,
    501                 EmailContent.Message.MAILBOX_KEY + "=?", new String[] { Long.toString(outboxId) },
    502                 null);
    503         try {
    504             // 2.  exit early
    505             if (c.getCount() <= 0) {
    506                 return;
    507             }
    508             final Sender sender = Sender.getInstance(context, account);
    509             final Store remoteStore = Store.getInstance(account, context);
    510             final ContentValues moveToSentValues;
    511             if (remoteStore.requireCopyMessageToSentFolder()) {
    512                 Mailbox sentFolder =
    513                     Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_SENT);
    514                 moveToSentValues = new ContentValues();
    515                 moveToSentValues.put(MessageColumns.MAILBOX_KEY, sentFolder.mId);
    516             } else {
    517                 moveToSentValues = null;
    518             }
    519 
    520             // 3.  loop through the available messages and send them
    521             while (c.moveToNext()) {
    522                 final long messageId;
    523                 if (moveToSentValues != null) {
    524                     moveToSentValues.remove(EmailContent.MessageColumns.FLAGS);
    525                 }
    526                 try {
    527                     messageId = c.getLong(0);
    528                     // Don't send messages with unloaded attachments
    529                     if (Utility.hasUnloadedAttachments(context, messageId)) {
    530                         if (MailActivityEmail.DEBUG) {
    531                             LogUtils.d(Logging.LOG_TAG, "Can't send #" + messageId +
    532                                     "; unloaded attachments");
    533                         }
    534                         continue;
    535                     }
    536                     sender.sendMessage(messageId);
    537                 } catch (MessagingException me) {
    538                     // report error for this message, but keep trying others
    539                     if (me instanceof AuthenticationFailedException) {
    540                         nc.showLoginFailedNotification(account.mId);
    541                     }
    542                     continue;
    543                 }
    544                 // 4. move to sent, or delete
    545                 final Uri syncedUri =
    546                     ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI, messageId);
    547                 // Delete all cached files
    548                 AttachmentUtilities.deleteAllCachedAttachmentFiles(context, account.mId, messageId);
    549                 if (moveToSentValues != null) {
    550                     // If this is a forwarded message and it has attachments, delete them, as they
    551                     // duplicate information found elsewhere (on the server).  This saves storage.
    552                     final EmailContent.Message msg =
    553                         EmailContent.Message.restoreMessageWithId(context, messageId);
    554                     if ((msg.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0) {
    555                         AttachmentUtilities.deleteAllAttachmentFiles(context, account.mId,
    556                                 messageId);
    557                     }
    558                     final int flags = msg.mFlags & ~(EmailContent.Message.FLAG_TYPE_REPLY |
    559                             EmailContent.Message.FLAG_TYPE_FORWARD |
    560                             EmailContent.Message.FLAG_TYPE_REPLY_ALL |
    561                             EmailContent.Message.FLAG_TYPE_ORIGINAL);
    562 
    563                     moveToSentValues.put(EmailContent.MessageColumns.FLAGS, flags);
    564                     resolver.update(syncedUri, moveToSentValues, null, null);
    565                 } else {
    566                     AttachmentUtilities.deleteAllAttachmentFiles(context, account.mId,
    567                             messageId);
    568                     final Uri uri =
    569                         ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId);
    570                     resolver.delete(uri, null, null);
    571                     resolver.delete(syncedUri, null, null);
    572                 }
    573             }
    574             nc.cancelLoginFailedNotification(account.mId);
    575         } catch (MessagingException me) {
    576             if (me instanceof AuthenticationFailedException) {
    577                 nc.showLoginFailedNotification(account.mId);
    578             }
    579         } finally {
    580             c.close();
    581         }
    582 
    583     }
    584 }
    585