Search in sources :

Example 26 with MessagingListener

use of com.fsck.k9.controller.MessagingListener in project k-9 by k9mail.

the class MessagingController method synchronizeMailboxSynchronous.

/**
     * Start foreground synchronization of the specified folder. This is generally only called
     * by synchronizeMailbox.
     * <p>
     * TODO Break this method up into smaller chunks.
     */
@VisibleForTesting
void synchronizeMailboxSynchronous(final Account account, final String folder, final MessagingListener listener, Folder providedRemoteFolder) {
    Folder remoteFolder = null;
    LocalFolder tLocalFolder = null;
    Timber.i("Synchronizing folder %s:%s", account.getDescription(), folder);
    for (MessagingListener l : getListeners(listener)) {
        l.synchronizeMailboxStarted(account, folder);
    }
    /*
         * We don't ever sync the Outbox or errors folder
         */
    if (folder.equals(account.getOutboxFolderName()) || folder.equals(account.getErrorFolderName())) {
        for (MessagingListener l : getListeners(listener)) {
            l.synchronizeMailboxFinished(account, folder, 0, 0);
        }
        return;
    }
    Exception commandException = null;
    try {
        Timber.d("SYNC: About to process pending commands for account %s", account.getDescription());
        try {
            processPendingCommandsSynchronous(account);
        } catch (Exception e) {
            addErrorMessage(account, null, e);
            Timber.e(e, "Failure processing command, but allow message sync attempt");
            commandException = e;
        }
        /*
             * Get the message list from the local store and create an index of
             * the uids within the list.
             */
        Timber.v("SYNC: About to get local folder %s", folder);
        final LocalStore localStore = account.getLocalStore();
        tLocalFolder = localStore.getFolder(folder);
        final LocalFolder localFolder = tLocalFolder;
        localFolder.open(Folder.OPEN_MODE_RW);
        localFolder.updateLastUid();
        Map<String, Long> localUidMap = localFolder.getAllMessagesAndEffectiveDates();
        if (providedRemoteFolder != null) {
            Timber.v("SYNC: using providedRemoteFolder %s", folder);
            remoteFolder = providedRemoteFolder;
        } else {
            Store remoteStore = account.getRemoteStore();
            Timber.v("SYNC: About to get remote folder %s", folder);
            remoteFolder = remoteStore.getFolder(folder);
            if (!verifyOrCreateRemoteSpecialFolder(account, folder, remoteFolder, listener)) {
                return;
            }
            /*
                 * Synchronization process:
                 *
                Open the folder
                Upload any local messages that are marked as PENDING_UPLOAD (Drafts, Sent, Trash)
                Get the message count
                Get the list of the newest K9.DEFAULT_VISIBLE_LIMIT messages
                getMessages(messageCount - K9.DEFAULT_VISIBLE_LIMIT, messageCount)
                See if we have each message locally, if not fetch it's flags and envelope
                Get and update the unread count for the folder
                Update the remote flags of any messages we have locally with an internal date newer than the remote message.
                Get the current flags for any messages we have locally but did not just download
                Update local flags
                For any message we have locally but not remotely, delete the local message to keep cache clean.
                Download larger parts of any new messages.
                (Optional) Download small attachments in the background.
                 */
            /*
                 * Open the remote folder. This pre-loads certain metadata like message count.
                 */
            Timber.v("SYNC: About to open remote folder %s", folder);
            remoteFolder.open(Folder.OPEN_MODE_RW);
            if (Expunge.EXPUNGE_ON_POLL == account.getExpungePolicy()) {
                Timber.d("SYNC: Expunging folder %s:%s", account.getDescription(), folder);
                remoteFolder.expunge();
            }
        }
        notificationController.clearAuthenticationErrorNotification(account, true);
        /*
             * Get the remote message count.
             */
        int remoteMessageCount = remoteFolder.getMessageCount();
        int visibleLimit = localFolder.getVisibleLimit();
        if (visibleLimit < 0) {
            visibleLimit = K9.DEFAULT_VISIBLE_LIMIT;
        }
        final List<Message> remoteMessages = new ArrayList<>();
        Map<String, Message> remoteUidMap = new HashMap<>();
        Timber.v("SYNC: Remote message count for folder %s is %d", folder, remoteMessageCount);
        final Date earliestDate = account.getEarliestPollDate();
        long earliestTimestamp = earliestDate != null ? earliestDate.getTime() : 0L;
        int remoteStart = 1;
        if (remoteMessageCount > 0) {
            /* Message numbers start at 1.  */
            if (visibleLimit > 0) {
                remoteStart = Math.max(0, remoteMessageCount - visibleLimit) + 1;
            } else {
                remoteStart = 1;
            }
            Timber.v("SYNC: About to get messages %d through %d for folder %s", remoteStart, remoteMessageCount, folder);
            final AtomicInteger headerProgress = new AtomicInteger(0);
            for (MessagingListener l : getListeners(listener)) {
                l.synchronizeMailboxHeadersStarted(account, folder);
            }
            List<? extends Message> remoteMessageArray = remoteFolder.getMessages(remoteStart, remoteMessageCount, earliestDate, null);
            int messageCount = remoteMessageArray.size();
            for (Message thisMess : remoteMessageArray) {
                headerProgress.incrementAndGet();
                for (MessagingListener l : getListeners(listener)) {
                    l.synchronizeMailboxHeadersProgress(account, folder, headerProgress.get(), messageCount);
                }
                Long localMessageTimestamp = localUidMap.get(thisMess.getUid());
                if (localMessageTimestamp == null || localMessageTimestamp >= earliestTimestamp) {
                    remoteMessages.add(thisMess);
                    remoteUidMap.put(thisMess.getUid(), thisMess);
                }
            }
            Timber.v("SYNC: Got %d messages for folder %s", remoteUidMap.size(), folder);
            for (MessagingListener l : getListeners(listener)) {
                l.synchronizeMailboxHeadersFinished(account, folder, headerProgress.get(), remoteUidMap.size());
            }
        } else if (remoteMessageCount < 0) {
            throw new Exception("Message count " + remoteMessageCount + " for folder " + folder);
        }
        /*
             * Remove any messages that are in the local store but no longer on the remote store or are too old
             */
        MoreMessages moreMessages = localFolder.getMoreMessages();
        if (account.syncRemoteDeletions()) {
            List<String> destroyMessageUids = new ArrayList<>();
            for (String localMessageUid : localUidMap.keySet()) {
                if (remoteUidMap.get(localMessageUid) == null) {
                    destroyMessageUids.add(localMessageUid);
                }
            }
            List<LocalMessage> destroyMessages = localFolder.getMessagesByUids(destroyMessageUids);
            if (!destroyMessageUids.isEmpty()) {
                moreMessages = MoreMessages.UNKNOWN;
                localFolder.destroyMessages(destroyMessages);
                for (Message destroyMessage : destroyMessages) {
                    for (MessagingListener l : getListeners(listener)) {
                        l.synchronizeMailboxRemovedMessage(account, folder, destroyMessage);
                    }
                }
            }
        }
        // noinspection UnusedAssignment, free memory early? (better break up the method!)
        localUidMap = null;
        if (moreMessages == MoreMessages.UNKNOWN) {
            updateMoreMessages(remoteFolder, localFolder, earliestDate, remoteStart);
        }
        /*
             * Now we download the actual content of messages.
             */
        int newMessages = downloadMessages(account, remoteFolder, localFolder, remoteMessages, false, true);
        int unreadMessageCount = localFolder.getUnreadMessageCount();
        for (MessagingListener l : getListeners()) {
            l.folderStatusChanged(account, folder, unreadMessageCount);
        }
        /* Notify listeners that we're finally done. */
        localFolder.setLastChecked(System.currentTimeMillis());
        localFolder.setStatus(null);
        Timber.d("Done synchronizing folder %s:%s @ %tc with %d new messages", account.getDescription(), folder, System.currentTimeMillis(), newMessages);
        for (MessagingListener l : getListeners(listener)) {
            l.synchronizeMailboxFinished(account, folder, remoteMessageCount, newMessages);
        }
        if (commandException != null) {
            String rootMessage = getRootCauseMessage(commandException);
            Timber.e("Root cause failure in %s:%s was '%s'", account.getDescription(), tLocalFolder.getName(), rootMessage);
            localFolder.setStatus(rootMessage);
            for (MessagingListener l : getListeners(listener)) {
                l.synchronizeMailboxFailed(account, folder, rootMessage);
            }
        }
        Timber.i("Done synchronizing folder %s:%s", account.getDescription(), folder);
    } catch (AuthenticationFailedException e) {
        handleAuthenticationFailure(account, true);
        for (MessagingListener l : getListeners(listener)) {
            l.synchronizeMailboxFailed(account, folder, "Authentication failure");
        }
    } catch (Exception e) {
        Timber.e(e, "synchronizeMailbox");
        // If we don't set the last checked, it can try too often during
        // failure conditions
        String rootMessage = getRootCauseMessage(e);
        if (tLocalFolder != null) {
            try {
                tLocalFolder.setStatus(rootMessage);
                tLocalFolder.setLastChecked(System.currentTimeMillis());
            } catch (MessagingException me) {
                Timber.e(e, "Could not set last checked on folder %s:%s", account.getDescription(), tLocalFolder.getName());
            }
        }
        for (MessagingListener l : getListeners(listener)) {
            l.synchronizeMailboxFailed(account, folder, rootMessage);
        }
        notifyUserIfCertificateProblem(account, e, true);
        addErrorMessage(account, null, e);
        Timber.e("Failed synchronizing folder %s:%s @ %tc", account.getDescription(), folder, System.currentTimeMillis());
    } finally {
        if (providedRemoteFolder == null) {
            closeFolder(remoteFolder);
        }
        closeFolder(tLocalFolder);
    }
}
Also used : LocalMessage(com.fsck.k9.mailstore.LocalMessage) MimeMessage(com.fsck.k9.mail.internet.MimeMessage) Message(com.fsck.k9.mail.Message) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) AuthenticationFailedException(com.fsck.k9.mail.AuthenticationFailedException) ArrayList(java.util.ArrayList) LocalStore(com.fsck.k9.mailstore.LocalStore) Store(com.fsck.k9.mail.Store) Pop3Store(com.fsck.k9.mail.store.pop3.Pop3Store) LocalStore(com.fsck.k9.mailstore.LocalStore) Folder(com.fsck.k9.mail.Folder) LocalFolder(com.fsck.k9.mailstore.LocalFolder) LocalMessage(com.fsck.k9.mailstore.LocalMessage) MessagingException(com.fsck.k9.mail.MessagingException) CertificateValidationException(com.fsck.k9.mail.CertificateValidationException) UnavailableStorageException(com.fsck.k9.mailstore.UnavailableStorageException) IOException(java.io.IOException) MessagingException(com.fsck.k9.mail.MessagingException) AuthenticationFailedException(com.fsck.k9.mail.AuthenticationFailedException) SuppressLint(android.annotation.SuppressLint) Date(java.util.Date) LocalFolder(com.fsck.k9.mailstore.LocalFolder) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) MoreMessages(com.fsck.k9.mailstore.LocalFolder.MoreMessages) VisibleForTesting(android.support.annotation.VisibleForTesting)

Example 27 with MessagingListener

use of com.fsck.k9.controller.MessagingListener in project k-9 by k9mail.

the class MessagingController method moveOrCopyMessageSynchronous.

private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, final List<? extends Message> inMessages, final String destFolder, final boolean isCopy) {
    try {
        LocalStore localStore = account.getLocalStore();
        Store remoteStore = account.getRemoteStore();
        if (!isCopy && (!remoteStore.isMoveCapable() || !localStore.isMoveCapable())) {
            return;
        }
        if (isCopy && (!remoteStore.isCopyCapable() || !localStore.isCopyCapable())) {
            return;
        }
        LocalFolder localSrcFolder = localStore.getFolder(srcFolder);
        Folder localDestFolder = localStore.getFolder(destFolder);
        boolean unreadCountAffected = false;
        List<String> uids = new LinkedList<>();
        for (Message message : inMessages) {
            String uid = message.getUid();
            if (!uid.startsWith(K9.LOCAL_UID_PREFIX)) {
                uids.add(uid);
            }
            if (!unreadCountAffected && !message.isSet(Flag.SEEN)) {
                unreadCountAffected = true;
            }
        }
        List<LocalMessage> messages = localSrcFolder.getMessagesByUids(uids);
        if (messages.size() > 0) {
            Map<String, Message> origUidMap = new HashMap<>();
            for (Message message : messages) {
                origUidMap.put(message.getUid(), message);
            }
            Timber.i("moveOrCopyMessageSynchronous: source folder = %s, %d messages, destination folder = %s, " + "isCopy = %s", srcFolder, messages.size(), destFolder, isCopy);
            Map<String, String> uidMap;
            if (isCopy) {
                FetchProfile fp = new FetchProfile();
                fp.add(Item.ENVELOPE);
                fp.add(Item.BODY);
                localSrcFolder.fetch(messages, fp, null);
                uidMap = localSrcFolder.copyMessages(messages, localDestFolder);
                if (unreadCountAffected) {
                    // If this copy operation changes the unread count in the destination
                    // folder, notify the listeners.
                    int unreadMessageCount = localDestFolder.getUnreadMessageCount();
                    for (MessagingListener l : getListeners()) {
                        l.folderStatusChanged(account, destFolder, unreadMessageCount);
                    }
                }
            } else {
                uidMap = localSrcFolder.moveMessages(messages, localDestFolder);
                for (Entry<String, Message> entry : origUidMap.entrySet()) {
                    String origUid = entry.getKey();
                    Message message = entry.getValue();
                    for (MessagingListener l : getListeners()) {
                        l.messageUidChanged(account, srcFolder, origUid, message.getUid());
                    }
                }
                unsuppressMessages(account, messages);
                if (unreadCountAffected) {
                    // If this move operation changes the unread count, notify the listeners
                    // that the unread count changed in both the source and destination folder.
                    int unreadMessageCountSrc = localSrcFolder.getUnreadMessageCount();
                    int unreadMessageCountDest = localDestFolder.getUnreadMessageCount();
                    for (MessagingListener l : getListeners()) {
                        l.folderStatusChanged(account, srcFolder, unreadMessageCountSrc);
                        l.folderStatusChanged(account, destFolder, unreadMessageCountDest);
                    }
                }
            }
            List<String> origUidKeys = new ArrayList<>(origUidMap.keySet());
            queueMoveOrCopy(account, srcFolder, destFolder, isCopy, origUidKeys, uidMap);
        }
        processPendingCommands(account);
    } catch (UnavailableStorageException e) {
        Timber.i("Failed to move/copy message because storage is not available - trying again later.");
        throw new UnavailableAccountException(e);
    } catch (MessagingException me) {
        addErrorMessage(account, null, me);
        throw new RuntimeException("Error moving message", me);
    }
}
Also used : LocalMessage(com.fsck.k9.mailstore.LocalMessage) FetchProfile(com.fsck.k9.mail.FetchProfile) LocalMessage(com.fsck.k9.mailstore.LocalMessage) MimeMessage(com.fsck.k9.mail.internet.MimeMessage) Message(com.fsck.k9.mail.Message) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) MessagingException(com.fsck.k9.mail.MessagingException) UnavailableStorageException(com.fsck.k9.mailstore.UnavailableStorageException) ArrayList(java.util.ArrayList) LocalStore(com.fsck.k9.mailstore.LocalStore) Store(com.fsck.k9.mail.Store) Pop3Store(com.fsck.k9.mail.store.pop3.Pop3Store) LocalStore(com.fsck.k9.mailstore.LocalStore) Folder(com.fsck.k9.mail.Folder) LocalFolder(com.fsck.k9.mailstore.LocalFolder) LinkedList(java.util.LinkedList) SuppressLint(android.annotation.SuppressLint) LocalFolder(com.fsck.k9.mailstore.LocalFolder)

Example 28 with MessagingListener

use of com.fsck.k9.controller.MessagingListener in project k-9 by k9mail.

the class MessagingController method loadSearchResultsSynchronous.

private void loadSearchResultsSynchronous(List<Message> messages, LocalFolder localFolder, Folder remoteFolder, MessagingListener listener) throws MessagingException {
    final FetchProfile header = new FetchProfile();
    header.add(FetchProfile.Item.FLAGS);
    header.add(FetchProfile.Item.ENVELOPE);
    final FetchProfile structure = new FetchProfile();
    structure.add(FetchProfile.Item.STRUCTURE);
    int i = 0;
    for (Message message : messages) {
        i++;
        LocalMessage localMsg = localFolder.getMessage(message.getUid());
        if (localMsg == null) {
            remoteFolder.fetch(Collections.singletonList(message), header, null);
            //fun fact: ImapFolder.fetch can't handle getting STRUCTURE at same time as headers
            remoteFolder.fetch(Collections.singletonList(message), structure, null);
            localFolder.appendMessages(Collections.singletonList(message));
            localMsg = localFolder.getMessage(message.getUid());
        }
    }
}
Also used : LocalMessage(com.fsck.k9.mailstore.LocalMessage) FetchProfile(com.fsck.k9.mail.FetchProfile) LocalMessage(com.fsck.k9.mailstore.LocalMessage) MimeMessage(com.fsck.k9.mail.internet.MimeMessage) Message(com.fsck.k9.mail.Message) SuppressLint(android.annotation.SuppressLint)

Example 29 with MessagingListener

use of com.fsck.k9.controller.MessagingListener in project k-9 by k9mail.

the class MessagingController method downloadSmallMessages.

private <T extends Message> void downloadSmallMessages(final Account account, final Folder<T> remoteFolder, final LocalFolder localFolder, List<T> smallMessages, final AtomicInteger progress, final int unreadBeforeStart, final AtomicInteger newMessages, final int todo, FetchProfile fp) throws MessagingException {
    final String folder = remoteFolder.getName();
    final Date earliestDate = account.getEarliestPollDate();
    Timber.d("SYNC: Fetching %d small messages for folder %s", smallMessages.size(), folder);
    remoteFolder.fetch(smallMessages, fp, new MessageRetrievalListener<T>() {

        @Override
        public void messageFinished(final T message, int number, int ofTotal) {
            try {
                if (!shouldImportMessage(account, message, earliestDate)) {
                    progress.incrementAndGet();
                    return;
                }
                // Store the updated message locally
                final LocalMessage localMessage = localFolder.storeSmallMessage(message, new Runnable() {

                    @Override
                    public void run() {
                        progress.incrementAndGet();
                    }
                });
                // not marked as read.
                if (!localMessage.isSet(Flag.SEEN)) {
                    newMessages.incrementAndGet();
                }
                Timber.v("About to notify listeners that we got a new small message %s:%s:%s", account, folder, message.getUid());
                // Update the listener with what we've found
                for (MessagingListener l : getListeners()) {
                    l.synchronizeMailboxProgress(account, folder, progress.get(), todo);
                    if (!localMessage.isSet(Flag.SEEN)) {
                        l.synchronizeMailboxNewMessage(account, folder, localMessage);
                    }
                }
                if (shouldNotifyForMessage(account, localFolder, message)) {
                    // Notify with the localMessage so that we don't have to recalculate the content preview.
                    notificationController.addNewMailNotification(account, localMessage, unreadBeforeStart);
                }
            } catch (MessagingException me) {
                addErrorMessage(account, null, me);
                Timber.e(me, "SYNC: fetch small messages");
            }
        }

        @Override
        public void messageStarted(String uid, int number, int ofTotal) {
        }

        @Override
        public void messagesFinished(int total) {
        }
    });
    Timber.d("SYNC: Done fetching small messages for folder %s", folder);
}
Also used : LocalMessage(com.fsck.k9.mailstore.LocalMessage) MessagingException(com.fsck.k9.mail.MessagingException) Date(java.util.Date) SuppressLint(android.annotation.SuppressLint)

Example 30 with MessagingListener

use of com.fsck.k9.controller.MessagingListener in project k-9 by k9mail.

the class MessagingController method setFlagSynchronous.

private void setFlagSynchronous(final Account account, final List<Long> ids, final Flag flag, final boolean newState, final boolean threadedList) {
    LocalStore localStore;
    try {
        localStore = account.getLocalStore();
    } catch (MessagingException e) {
        Timber.e(e, "Couldn't get LocalStore instance");
        return;
    }
    // can be updated with the new state.
    try {
        if (threadedList) {
            localStore.setFlagForThreads(ids, flag, newState);
            removeFlagForThreadsFromCache(account, ids, flag);
        } else {
            localStore.setFlag(ids, flag, newState);
            removeFlagFromCache(account, ids, flag);
        }
    } catch (MessagingException e) {
        Timber.e(e, "Couldn't set flags in local database");
    }
    // Read folder name and UID of messages from the database
    Map<String, List<String>> folderMap;
    try {
        folderMap = localStore.getFoldersAndUids(ids, threadedList);
    } catch (MessagingException e) {
        Timber.e(e, "Couldn't get folder name and UID of messages");
        return;
    }
    // Loop over all folders
    for (Entry<String, List<String>> entry : folderMap.entrySet()) {
        String folderName = entry.getKey();
        // Notify listeners of changed folder status
        LocalFolder localFolder = localStore.getFolder(folderName);
        try {
            int unreadMessageCount = localFolder.getUnreadMessageCount();
            for (MessagingListener l : getListeners()) {
                l.folderStatusChanged(account, folderName, unreadMessageCount);
            }
        } catch (MessagingException e) {
            Timber.w(e, "Couldn't get unread count for folder: %s", folderName);
        }
        // TODO: Skip the remote part for all local-only folders
        if (account.getErrorFolderName().equals(folderName)) {
            continue;
        }
        // Send flag change to server
        queueSetFlag(account, folderName, newState, flag, entry.getValue());
        processPendingCommands(account);
    }
}
Also used : LocalFolder(com.fsck.k9.mailstore.LocalFolder) MessagingException(com.fsck.k9.mail.MessagingException) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList) LocalStore(com.fsck.k9.mailstore.LocalStore) SuppressLint(android.annotation.SuppressLint)

Aggregations

MessagingException (com.fsck.k9.mail.MessagingException)25 LocalFolder (com.fsck.k9.mailstore.LocalFolder)20 LocalStore (com.fsck.k9.mailstore.LocalStore)20 LocalMessage (com.fsck.k9.mailstore.LocalMessage)18 UnavailableStorageException (com.fsck.k9.mailstore.UnavailableStorageException)15 SuppressLint (android.annotation.SuppressLint)14 Folder (com.fsck.k9.mail.Folder)14 Message (com.fsck.k9.mail.Message)14 MimeMessage (com.fsck.k9.mail.internet.MimeMessage)14 AuthenticationFailedException (com.fsck.k9.mail.AuthenticationFailedException)13 CertificateValidationException (com.fsck.k9.mail.CertificateValidationException)13 IOException (java.io.IOException)13 Store (com.fsck.k9.mail.Store)12 Pop3Store (com.fsck.k9.mail.store.pop3.Pop3Store)12 ArrayList (java.util.ArrayList)9 FetchProfile (com.fsck.k9.mail.FetchProfile)7 VisibleForTesting (android.support.annotation.VisibleForTesting)6 Account (com.fsck.k9.Account)6 Date (java.util.Date)6 SearchAccount (com.fsck.k9.search.SearchAccount)5