/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Messaging Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "imapservice.h" #ifndef QMF_NO_MESSAGE_SERVICE_EDITOR #include "imapsettings.h" #endif #include "imapconfiguration.h" #include "imapstrategy.h" #include #include #include #include #include #include namespace { const QString serviceKey("imap4"); QMailFolderId mailboxId(const QMailAccountId &accountId, const QString &path) { QMailFolderIdList folderIds = QMailStore::instance()->queryFolders(QMailFolderKey::parentAccountId(accountId) & QMailFolderKey::path(path)); if (folderIds.count() == 1) return folderIds.first(); return QMailFolderId(); } QMailFolderIdList statusFolders(const QMailAccountId &accountId, quint64 mask) { return QMailStore::instance()->queryFolders(QMailFolderKey::parentAccountId(accountId) & QMailFolderKey::status(mask)); } } class ImapService::Source : public QMailMessageSource { Q_OBJECT public: Source(ImapService *service) : QMailMessageSource(service), _service(service), _queuedMailCheckInProgress(false), _mailCheckPhase(RetrieveFolders), _unavailable(false), _synchronizing(false), _setMask(0), _unsetMask(0) { connect(&_intervalTimer, SIGNAL(timeout()), this, SLOT(intervalCheck())); connect(&_pushIntervalTimer, SIGNAL(timeout()), this, SLOT(pushIntervalCheck())); connect(&_strategyExpiryTimer, SIGNAL(timeout()), this, SLOT(expireStrategy())); } void initClientConnections() { connect(_service->_client, SIGNAL(allMessagesReceived()), this, SIGNAL(newMessagesAvailable())); connect(_service->_client, SIGNAL(messageCopyCompleted(QMailMessage&, QMailMessage)), this, SLOT(messageCopyCompleted(QMailMessage&, QMailMessage))); connect(_service->_client, SIGNAL(messageActionCompleted(QString)), this, SLOT(messageActionCompleted(QString))); connect(_service->_client, SIGNAL(retrievalCompleted()), this, SLOT(retrievalCompleted())); connect(_service->_client, SIGNAL(idleNewMailNotification(QMailFolderId)), this, SLOT(queueMailCheck(QMailFolderId))); connect(_service->_client, SIGNAL(idleFlagsChangedNotification(QMailFolderId)), this, SLOT(queueFlagsChangedCheck(QMailFolderId))); connect(_service->_client, SIGNAL(matchingMessageIds(QMailMessageIdList)), this, SIGNAL(matchingMessageIds(QMailMessageIdList))); connect(_service->_client, SIGNAL(remainingMessagesCount(uint)), this, SIGNAL(remainingMessagesCount(uint))); connect(_service->_client, SIGNAL(messagesCount(uint)), this, SIGNAL(messagesCount(uint))); } void setIntervalTimer(int interval) { _intervalTimer.stop(); if (interval > 0) { _intervalTimer.start(interval*1000*60); // interval minutes } } void setPushIntervalTimer(int pushInterval) { _pushIntervalTimer.stop(); if (pushInterval > 0) { _pushIntervalTimer.start(pushInterval*1000*60); // interval minutes } } virtual QMailStore::MessageRemovalOption messageRemovalOption() const { return QMailStore::CreateRemovalRecord; } signals: void messageActionCompleted(const QMailMessageIdList &ids); public slots: virtual bool retrieveFolderList(const QMailAccountId &accountId, const QMailFolderId &folderId, bool descending); virtual bool retrieveMessageLists(const QMailAccountId &accountId, const QMailFolderIdList &folderIds, uint minimum, const QMailMessageSortKey &sort); virtual bool retrieveMessageList(const QMailAccountId &accountId, const QMailFolderId &folderId, uint minimum, const QMailMessageSortKey &sort); virtual bool retrieveMessageLists(const QMailAccountId &accountId, const QMailFolderIdList &_folderIds, uint minimum, const QMailMessageSortKey &sort, bool retrieveAll); virtual bool retrieveMessages(const QMailMessageIdList &messageIds, QMailRetrievalAction::RetrievalSpecification spec); virtual bool retrieveMessagePart(const QMailMessagePart::Location &partLocation); virtual bool retrieveMessageRange(const QMailMessageId &messageId, uint minimum); virtual bool retrieveMessagePartRange(const QMailMessagePart::Location &partLocation, uint minimum); virtual bool retrieveAll(const QMailAccountId &accountId); virtual bool exportUpdates(const QMailAccountId &accountId); virtual bool synchronize(const QMailAccountId &accountId); virtual bool deleteMessages(const QMailMessageIdList &ids); virtual bool copyMessages(const QMailMessageIdList &ids, const QMailFolderId &destinationId); virtual bool moveMessages(const QMailMessageIdList &ids, const QMailFolderId &destinationId); virtual bool flagMessages(const QMailMessageIdList &ids, quint64 setMask, quint64 unsetMask); virtual bool createFolder(const QString &name, const QMailAccountId &accountId, const QMailFolderId &parentId); virtual bool deleteFolder(const QMailFolderId &folderId); virtual bool renameFolder(const QMailFolderId &folderId, const QString &name); virtual bool searchMessages(const QMailMessageKey &searchCriteria, const QString &bodyText, const QMailMessageSortKey &sort); virtual bool searchMessages(const QMailMessageKey &searchCriteria, const QString &bodyText, quint64 limit, const QMailMessageSortKey &sort); virtual bool searchMessages(const QMailMessageKey &searchCriteria, const QString &bodyText, quint64 limit, const QMailMessageSortKey &sort, bool count); virtual bool countMessages(const QMailMessageKey &searchCriteria, const QString &bodyText); virtual bool cancelSearch(); virtual bool prepareMessages(const QList > &ids); void messageCopyCompleted(QMailMessage &message, const QMailMessage &original); void messageActionCompleted(const QString &uid); void retrievalCompleted(); void retrievalTerminated(); void intervalCheck(); void pushIntervalCheck(); void queueMailCheck(QMailFolderId folderId); void queueFlagsChangedCheck(QMailFolderId folderId); void resetExpiryTimer(); void expireStrategy(); void emitActionSuccessfullyCompleted(); private: bool doDelete(const QMailMessageIdList & ids); virtual bool setStrategy(ImapStrategy *strategy, const char *signal = 0); virtual void appendStrategy(ImapStrategy *strategy, const char *signal = 0); virtual bool initiateStrategy(); void queueDisconnectedOperations(const QMailAccountId &accountId); enum MailCheckPhase { RetrieveFolders = 0, RetrieveMessages, CheckFlags }; ImapService *_service; bool _queuedMailCheckInProgress; MailCheckPhase _mailCheckPhase; QMailFolderId _mailCheckFolderId; bool _unavailable; bool _synchronizing; QTimer _intervalTimer; QTimer _pushIntervalTimer; QList _queuedFolders; // require new mail check QList _queuedFoldersFullCheck; // require flags check also quint64 _setMask; quint64 _unsetMask; QList > _pendingStrategies; QTimer _strategyExpiryTimer; // Required to expire interval mail check triggered by push email }; bool ImapService::Source::retrieveFolderList(const QMailAccountId &accountId, const QMailFolderId &folderId, bool descending) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } _service->_client->strategyContext()->foldersOnlyStrategy.clearSelection(); _service->_client->strategyContext()->foldersOnlyStrategy.setBase(folderId); _service->_client->strategyContext()->foldersOnlyStrategy.setQuickList(!folderId.isValid()); _service->_client->strategyContext()->foldersOnlyStrategy.setDescending(descending); appendStrategy(&_service->_client->strategyContext()->foldersOnlyStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::retrieveMessageLists(const QMailAccountId &accountId, const QMailFolderIdList &folderIds, uint minimum, const QMailMessageSortKey &sort) { QMailFolderIdList ids; foreach (const QMailFolderId &id, folderIds) { if (QMailFolder(id).status() & QMailFolder::MessagesPermitted) ids.append(id); } if (ids.isEmpty()) { QTimer::singleShot(0, this, SLOT(emitActionSuccessfullyCompleted())); return true; } return retrieveMessageLists(accountId, ids, minimum, sort, true /* accountCheck */); } bool ImapService::Source::retrieveMessageList(const QMailAccountId &accountId, const QMailFolderId &folderId, uint minimum, const QMailMessageSortKey &sort) { if (folderId.isValid()) { // Folder check return retrieveMessageLists(accountId, QMailFolderIdList() << folderId, minimum, sort, false); } // Full account check return retrieveMessageLists(accountId, QMailFolderIdList(), minimum, sort, true); } bool ImapService::Source::retrieveMessageLists(const QMailAccountId &accountId, const QMailFolderIdList &_folderIds, uint minimum, const QMailMessageSortKey &sort, bool accountCheck) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } if (!sort.isEmpty()) { qWarning() << "IMAP Search sorting not yet implemented!"; } QMailFolderIdList folderIds; uint adjustedMinimum = minimum ? minimum : INT_MAX; // zero means retrieve all mail _service->_client->strategyContext()->retrieveMessageListStrategy.clearSelection(); _service->_client->strategyContext()->retrieveMessageListStrategy.setMinimum(adjustedMinimum); if (!_folderIds.isEmpty()) { folderIds = _folderIds; } else { // Retrieve messages for all folders in the account that have undiscovered messages QMailFolderKey accountKey(QMailFolderKey::parentAccountId(accountId)); QMailFolderKey canSelectKey(QMailFolderKey::status(QMailFolder::MessagesPermitted)); QMailFolderKey filterKey(accountKey & canSelectKey); folderIds = QMailStore::instance()->queryFolders(filterKey, QMailFolderSortKey::id(Qt::AscendingOrder)); } // accountCheck false, just retrieve new mail or minimum mails whichever is more // accountCheck is true, also update status of messages on device, and detect removed messages _service->_client->strategyContext()->retrieveMessageListStrategy.setAccountCheck(accountCheck); _service->_client->strategyContext()->retrieveMessageListStrategy.setOperation(_service->_client->strategyContext(), QMailRetrievalAction::Auto); _service->_client->strategyContext()->retrieveMessageListStrategy.selectedFoldersAppend(folderIds); appendStrategy(&_service->_client->strategyContext()->retrieveMessageListStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::retrieveMessages(const QMailMessageIdList &messageIds, QMailRetrievalAction::RetrievalSpecification spec) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to retrieve")); return false; } if (spec == QMailRetrievalAction::Flags) { _service->_client->strategyContext()->updateMessagesFlagsStrategy.clearSelection(); _service->_client->strategyContext()->updateMessagesFlagsStrategy.selectedMailsAppend(messageIds); appendStrategy(&_service->_client->strategyContext()->updateMessagesFlagsStrategy); if(!_unavailable) return initiateStrategy(); return true; } _service->_client->strategyContext()->selectedStrategy.clearSelection(); // Select the parts to be downloaded according to "spec". _service->_client->strategyContext()->selectedStrategy.setOperation( _service->_client->strategyContext(), spec); QMailMessageIdList completionList; QList > completionSectionList; foreach (const QMailMessageId &id, messageIds) { QMailMessage message(id); _service->_client->strategyContext()->selectedStrategy.prepareCompletionList( _service->_client->strategyContext(), message, completionList, completionSectionList); } _service->_client->strategyContext()->selectedStrategy.selectedMailsAppend(completionList); typedef QPair SectionDescription; foreach (const SectionDescription §ion, completionSectionList) { _service->_client->strategyContext()->selectedStrategy.selectedSectionsAppend(section.first, section.second); } appendStrategy(&_service->_client->strategyContext()->selectedStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::retrieveMessagePart(const QMailMessagePart::Location &partLocation) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!partLocation.containingMessageId().isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No message to retrieve")); return false; } if (!partLocation.isValid(false)) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No part specified")); return false; } if (!QMailMessage(partLocation.containingMessageId()).id().isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Invalid message specified")); return false; } QMailMessage msg(partLocation.containingMessageId()); if (!msg.contains(partLocation) || msg.partAt(partLocation).contentAvailable()) { // Already retrieved (or invalid) if (!_unavailable) QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } _service->_client->strategyContext()->selectedStrategy.clearSelection(); _service->_client->strategyContext()->selectedStrategy.setOperation(_service->_client->strategyContext(), QMailRetrievalAction::Content); _service->_client->strategyContext()->selectedStrategy.selectedSectionsAppend(partLocation); appendStrategy(&_service->_client->strategyContext()->selectedStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::retrieveMessageRange(const QMailMessageId &messageId, uint minimum) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!messageId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No message to retrieve")); return false; } if (!QMailMessage(messageId).id().isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Invalid message specified")); return false; } // Not tested yet if (!minimum) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No minimum specified")); return false; } QMailMessage msg(messageId); if (msg.contentAvailable()) { // Already retrieved if (!_unavailable) QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } QMailMessagePart::Location location; location.setContainingMessageId(messageId); _service->_client->strategyContext()->selectedStrategy.clearSelection(); _service->_client->strategyContext()->selectedStrategy.setOperation(_service->_client->strategyContext(), QMailRetrievalAction::Content); _service->_client->strategyContext()->selectedStrategy.selectedSectionsAppend(location, minimum); appendStrategy(&_service->_client->strategyContext()->selectedStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::retrieveMessagePartRange(const QMailMessagePart::Location &partLocation, uint minimum) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!partLocation.containingMessageId().isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No message to retrieve")); return false; } if (!partLocation.isValid(false)) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No part specified")); return false; } if (!QMailMessage(partLocation.containingMessageId()).id().isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Invalid message specified")); return false; } if (!minimum) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No minimum specified")); return false; } QMailMessage msg(partLocation.containingMessageId()); if (!msg.contains(partLocation) || msg.partAt(partLocation).contentAvailable()) { // Already retrieved (or invalid) if (!_unavailable) QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } _service->_client->strategyContext()->selectedStrategy.clearSelection(); _service->_client->strategyContext()->selectedStrategy.setOperation(_service->_client->strategyContext(), QMailRetrievalAction::Content); _service->_client->strategyContext()->selectedStrategy.selectedSectionsAppend(partLocation, minimum); appendStrategy(&_service->_client->strategyContext()->selectedStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::retrieveAll(const QMailAccountId &accountId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } _service->_client->strategyContext()->retrieveAllStrategy.clearSelection(); _service->_client->strategyContext()->retrieveAllStrategy.setBase(QMailFolderId()); _service->_client->strategyContext()->retrieveAllStrategy.setQuickList(false); _service->_client->strategyContext()->retrieveAllStrategy.setDescending(true); _service->_client->strategyContext()->retrieveAllStrategy.setOperation(_service->_client->strategyContext(), QMailRetrievalAction::Auto); appendStrategy(&_service->_client->strategyContext()->retrieveAllStrategy); if(!_unavailable) return initiateStrategy(); return true; } void ImapService::Source::queueDisconnectedOperations(const QMailAccountId &accountId) { //sync disconnected move and copy operations for account QMailFolderIdList folderList = QMailStore::instance()->queryFolders(QMailFolderKey::parentAccountId(accountId)); bool pendingDisconnectedOperations = false; _service->_client->strategyContext()->moveMessagesStrategy.clearSelection(); foreach(const QMailFolderId& folderId, folderList) { if (!folderId.isValid()) continue; QMailMessageKey movedIntoFolderKey(QMailDisconnected::destinationKey(folderId)); QMailMessageIdList movedMessages = QMailStore::instance()->queryMessages(movedIntoFolderKey); if (movedMessages.isEmpty()) continue; pendingDisconnectedOperations = true; _service->_client->strategyContext()->moveMessagesStrategy.appendMessageSet(movedMessages, folderId); } if (pendingDisconnectedOperations) appendStrategy(&_service->_client->strategyContext()->moveMessagesStrategy, SIGNAL(messagesMoved(QMailMessageIdList))); } bool ImapService::Source::exportUpdates(const QMailAccountId &accountId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } queueDisconnectedOperations(accountId); _service->_client->strategyContext()->exportUpdatesStrategy.clearSelection(); appendStrategy(&_service->_client->strategyContext()->exportUpdatesStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::synchronize(const QMailAccountId &accountId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } queueDisconnectedOperations(accountId); _service->_client->strategyContext()->synchronizeAccountStrategy.clearSelection(); _service->_client->strategyContext()->synchronizeAccountStrategy.setBase(QMailFolderId()); _service->_client->strategyContext()->synchronizeAccountStrategy.setQuickList(false); _service->_client->strategyContext()->synchronizeAccountStrategy.setDescending(true); _service->_client->strategyContext()->synchronizeAccountStrategy.setOperation(_service->_client->strategyContext(), QMailRetrievalAction::Auto); appendStrategy(&_service->_client->strategyContext()->synchronizeAccountStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::deleteMessages(const QMailMessageIdList &allIds) { // If a server crash has occurred duplicate messages may exist in the store. // A duplicate message is one that refers to the same serverUid as another message in the same account & folder. // Ensure that when a duplicate message is deleted no message is deleted from the server. QMailMessageKey::Properties props(QMailMessageKey::ServerUid | QMailMessageKey::Id); QStringList serverUids; QMailMessageIdList ids; QMailMessageIdList localIds; foreach (const QMailMessageMetaData &metaData, QMailStore::instance()->messagesMetaData(QMailMessageKey::id(allIds), props)) { if (!metaData.serverUid().isEmpty()) { serverUids.push_back(metaData.serverUid()); ids.push_back(metaData.id()); } else { localIds.append(metaData.id()); } } if (!localIds.isEmpty()) { if (!QMailMessageSource::deleteMessages(localIds)) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Could not delete messages")); return false; } if (ids.isEmpty()) return true; } Q_ASSERT(serverUids.size() == ids.size()); QMailMessageKey accountKey(QMailMessageKey::parentAccountId(_service->accountId())); int matching(QMailStore::instance()->countMessages(QMailMessageKey::serverUid(serverUids, QMailDataComparator::Includes) & accountKey)); Q_ASSERT(matching >= ids.size()); if (matching == ids.size()) { // no dupes, lets go return doDelete(ids); } else { QMailMessageIdList duplicateIds; QMailMessageIdList singularIds; for (int i(0) ; i < ids.size() ; ++i) { if (QMailStore::instance()->countMessages(QMailMessageKey::serverUid(serverUids[i]) & accountKey) > 1) { duplicateIds.push_back(ids[i]); } else { singularIds.push_back(ids[i]); } } Q_ASSERT(!duplicateIds.empty()); if (!QMailMessageSource::deleteMessages(duplicateIds)) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Could not delete messages")); return false; } return doDelete(singularIds); } } bool ImapService::Source::doDelete(const QMailMessageIdList &ids) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); if (imapCfg.canDeleteMail()) { // Delete the messages from the server _service->_client->strategyContext()->deleteMessagesStrategy.clearSelection(); _service->_client->strategyContext()->deleteMessagesStrategy.setLocalMessageRemoval(true); _service->_client->strategyContext()->deleteMessagesStrategy.selectedMailsAppend(ids); appendStrategy(&_service->_client->strategyContext()->deleteMessagesStrategy, SIGNAL(messagesDeleted(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } // Just delete the local copies return QMailMessageSource::deleteMessages(ids); } bool ImapService::Source::copyMessages(const QMailMessageIdList &messageIds, const QMailFolderId &destinationId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to copy")); return false; } if (!destinationId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Invalid destination folder")); return false; } QMailFolder destination(destinationId); if (destination.parentAccountId() == _service->accountId()) { _service->_client->strategyContext()->copyMessagesStrategy.clearSelection(); _service->_client->strategyContext()->copyMessagesStrategy.appendMessageSet(messageIds, destinationId); appendStrategy(&_service->_client->strategyContext()->copyMessagesStrategy, SIGNAL(messagesCopied(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } // Otherwise create local copies return QMailMessageSource::copyMessages(messageIds, destinationId); } bool ImapService::Source::moveMessages(const QMailMessageIdList &messageIds, const QMailFolderId &destinationId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to move")); return false; } if (!destinationId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Invalid destination folder")); return false; } QMailFolder destination(destinationId); if (destination.parentAccountId() == _service->accountId()) { _service->_client->strategyContext()->moveMessagesStrategy.clearSelection(); _service->_client->strategyContext()->moveMessagesStrategy.appendMessageSet(messageIds, destinationId); appendStrategy(&_service->_client->strategyContext()->moveMessagesStrategy, SIGNAL(messagesMoved(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } // Otherwise - if any of these messages are in folders on the server, we should remove them QMailMessageIdList serverMessages; // Do we need to remove these messages from the server? QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); if (imapCfg.canDeleteMail()) { serverMessages = QMailStore::instance()->queryMessages(QMailMessageKey::id(messageIds) & QMailMessageKey::parentAccountId(_service->accountId())); if (!serverMessages.isEmpty()) { // Delete the messages from the server _service->_client->strategyContext()->deleteMessagesStrategy.clearSelection(); _service->_client->strategyContext()->deleteMessagesStrategy.setLocalMessageRemoval(false); _service->_client->strategyContext()->deleteMessagesStrategy.selectedMailsAppend(serverMessages); appendStrategy(&_service->_client->strategyContext()->deleteMessagesStrategy); if(!_unavailable) initiateStrategy(); } } // Move the local copies QMailMessageMetaData metaData; metaData.setParentFolderId(destinationId); // Clear the server UID, because it won't refer to anything useful... metaData.setServerUid(QString()); QMailMessageKey idsKey(QMailMessageKey::id(messageIds)); if (!QMailStore::instance()->updateMessagesMetaData(idsKey, QMailMessageKey::ParentFolderId | QMailMessageKey::ServerUid, metaData)) { qWarning() << "Unable to update message metadata for move to folder:" << destinationId; } else { emit messagesMoved(messageIds); } if (serverMessages.isEmpty()) { QTimer::singleShot(0, this, SLOT(retrievalCompleted())); } return true; } bool ImapService::Source::flagMessages(const QMailMessageIdList &messageIds, quint64 setMask, quint64 unsetMask) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to flag")); return false; } if (!setMask && !unsetMask) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No flags to be applied")); return false; } // Update the local copy status immediately QMailMessageSource::modifyMessageFlags(messageIds, setMask, unsetMask); // See if there are any further actions to be taken QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); // Note: we can't do everything all at once - just perform the first change that we // identify, as a client can always perform the changes incrementally. if ((setMask & QMailMessage::Trash) || (unsetMask & QMailMessage::Trash)) { QMailFolderId trashId(QMailAccount(_service->accountId()).standardFolder(QMailFolder::TrashFolder)); if (trashId.isValid()) { _setMask = setMask; _unsetMask = unsetMask; if (setMask & QMailMessage::Trash) { _service->_client->strategyContext()->moveMessagesStrategy.clearSelection(); _service->_client->strategyContext()->moveMessagesStrategy.appendMessageSet(messageIds, trashId); appendStrategy(&_service->_client->strategyContext()->moveMessagesStrategy, SIGNAL(messagesFlagged(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } else if (_unsetMask & QMailMessage::Trash) { QMap destinationList; // These messages need to be restored to their previous locations destinationList = QMailDisconnected::restoreMap(messageIds); _service->_client->strategyContext()->moveMessagesStrategy.clearSelection(); QMap::const_iterator it = destinationList.begin(), end = destinationList.end(); for ( ; it != end; ++it) { _service->_client->strategyContext()->moveMessagesStrategy.appendMessageSet(it.value(), it.key()); } appendStrategy(&_service->_client->strategyContext()->moveMessagesStrategy, SIGNAL(messagesFlagged(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } } } if (setMask & QMailMessage::Sent) { QMailFolderId sentId(QMailAccount(_service->accountId()).standardFolder(QMailFolder::SentFolder)); if (sentId.isValid()) { _setMask = setMask; _unsetMask = unsetMask; QMailMessageIdList moveIds; QMailMessageIdList flagIds; QMailMessageKey key(QMailMessageKey::id(messageIds)); QMailMessageKey::Properties props(QMailMessageKey::Id | QMailMessageKey::ParentFolderId); foreach (const QMailMessageMetaData &metaData, QMailStore::instance()->messagesMetaData(key, props)) { // If the message is already in the correct location just update the flags to remove \Draft if (metaData.parentFolderId() == sentId) { flagIds.append(metaData.id()); } else { moveIds.append(metaData.id()); } } if (!flagIds.isEmpty()) { _service->_client->strategyContext()->flagMessagesStrategy.clearSelection(); _service->_client->strategyContext()->flagMessagesStrategy.setMessageFlags(MFlag_Draft, false); _service->_client->strategyContext()->flagMessagesStrategy.selectedMailsAppend(flagIds); appendStrategy(&_service->_client->strategyContext()->flagMessagesStrategy, SIGNAL(messagesFlagged(QMailMessageIdList))); } if (!moveIds.isEmpty()) { _service->_client->strategyContext()->moveMessagesStrategy.clearSelection(); _service->_client->strategyContext()->moveMessagesStrategy.appendMessageSet(moveIds, sentId); appendStrategy(&_service->_client->strategyContext()->moveMessagesStrategy, SIGNAL(messagesFlagged(QMailMessageIdList))); } if(!_unavailable) return initiateStrategy(); else return true; } } if (setMask & QMailMessage::Draft) { QMailFolderId draftId(QMailAccount(_service->accountId()).standardFolder(QMailFolder::DraftsFolder)); if (draftId.isValid()) { _setMask = setMask; _unsetMask = unsetMask; // Move these messages to the predefined location - if they're already in the drafts // folder, we still want to overwrite them with the current content in case it has been updated _service->_client->strategyContext()->moveMessagesStrategy.clearSelection(); _service->_client->strategyContext()->moveMessagesStrategy.appendMessageSet(messageIds, draftId); appendStrategy(&_service->_client->strategyContext()->moveMessagesStrategy, SIGNAL(messagesFlagged(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } } quint64 updatableFlags(QMailMessage::Replied | QMailMessage::RepliedAll | QMailMessage::Forwarded | QMailMessage::Read | QMailMessage::Important); if ((setMask & updatableFlags) || (unsetMask & updatableFlags)) { // We could hold on to these changes until exportUpdates instead... MessageFlags setFlags(0); MessageFlags unsetFlags(0); if (setMask & (QMailMessage::Replied | QMailMessage::RepliedAll)) { setFlags |= MFlag_Answered; } if (unsetMask & (QMailMessage::Replied | QMailMessage::RepliedAll)) { unsetFlags |= MFlag_Answered; } if (setMask & QMailMessage::Read) { setFlags |= MFlag_Seen; } if (unsetMask & QMailMessage::Read) { unsetFlags |= MFlag_Seen; } if (setMask & QMailMessage::Important) { setFlags |= MFlag_Flagged; } if (unsetMask & QMailMessage::Important) { unsetFlags |= MFlag_Flagged; } if ((setMask | unsetMask) & QMailMessage::Forwarded) { // We can only modify this flag if the folders support $Forwarded bool supportsForwarded(true); QMailMessageKey key(QMailMessageKey::id(messageIds)); QMailMessageKey::Properties props(QMailMessageKey::Id | QMailMessageKey::ParentFolderId); foreach (const QMailMessageMetaData &metaData, QMailStore::instance()->messagesMetaData(key, props, QMailStore::ReturnDistinct)) { QMailFolder folder(metaData.parentFolderId()); if (folder.customField("qmf-supports-forwarded").isEmpty()) { supportsForwarded = false; break; } } if (supportsForwarded) { if (setMask & QMailMessage::Forwarded) { setFlags |= MFlag_Forwarded; } if (unsetMask & QMailMessage::Forwarded) { unsetFlags |= MFlag_Forwarded; } } } if (setFlags || unsetFlags) { _service->_client->strategyContext()->flagMessagesStrategy.clearSelection(); if (setFlags) { _service->_client->strategyContext()->flagMessagesStrategy.setMessageFlags(setFlags, true); } if (unsetFlags) { _service->_client->strategyContext()->flagMessagesStrategy.setMessageFlags(unsetFlags, false); } _service->_client->strategyContext()->flagMessagesStrategy.selectedMailsAppend(messageIds); appendStrategy(&_service->_client->strategyContext()->flagMessagesStrategy, SIGNAL(messagesFlagged(QMailMessageIdList))); if(!_unavailable) return initiateStrategy(); return true; } } //ensure retrievalCompleted gets called when a strategy has not been used (i.e. local read flag change) //otherwise actionCompleted does not get signaled to messageserver and service becomes permanently unavailable if(!_unavailable) QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } bool ImapService::Source::createFolder(const QString &name, const QMailAccountId &accountId, const QMailFolderId &parentId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } //here we'll create a QMailFolder and give it to the strategy //if it is successful, we'll let it register it as a real folder in the QMailStore if(name.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Cannot create empty named folder")); return false; } _service->_client->strategyContext()->createFolderStrategy.createFolder(parentId, name); appendStrategy(&_service->_client->strategyContext()->createFolderStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::deleteFolder(const QMailFolderId &folderId) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if(!folderId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Deleting invalid folder")); return false; } // Don't delete messages that the user has moved out of the folder QMailFolder folder(folderId); queueDisconnectedOperations(folder.parentAccountId()); //remove remote copy _service->_client->strategyContext()->deleteFolderStrategy.deleteFolder(folderId); appendStrategy(&_service->_client->strategyContext()->deleteFolderStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::renameFolder(const QMailFolderId &folderId, const QString &name) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if(name.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Cannot rename to an empty folder")); return false; } if(!folderId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Cannot rename an invalid folder")); return false; } _service->_client->strategyContext()->renameFolderStrategy.renameFolder(folderId, name); appendStrategy(&_service->_client->strategyContext()->renameFolderStrategy); if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::searchMessages(const QMailMessageKey &searchCriteria, const QString &bodyText, const QMailMessageSortKey &sort) { QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); return searchMessages(searchCriteria, bodyText, imapCfg.searchLimit(), sort, false); } bool ImapService::Source::searchMessages(const QMailMessageKey &searchCriteria, const QString &bodyText, quint64 limit, const QMailMessageSortKey &sort) { QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); return searchMessages(searchCriteria, bodyText, limit, sort, false); } bool ImapService::Source::countMessages(const QMailMessageKey &searchCriteria, const QString &bodyText) { QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); return searchMessages(searchCriteria, bodyText, 0, QMailMessageSortKey(), true); } bool ImapService::Source::searchMessages(const QMailMessageKey &searchCriteria, const QString &bodyText, quint64 limit, const QMailMessageSortKey &sort, bool count) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if(searchCriteria.isEmpty() && bodyText.isEmpty()) { //we're not going to do an empty search (which returns all emails..) _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Empty search provided")); return false; } _service->_client->strategyContext()->searchMessageStrategy.searchArguments(searchCriteria, bodyText, limit, sort, count); appendStrategy(&_service->_client->strategyContext()->searchMessageStrategy); if(!_unavailable) initiateStrategy(); return true; } bool ImapService::Source::cancelSearch() { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } _service->_client->strategyContext()->searchMessageStrategy.cancelSearch(); appendStrategy(&_service->_client->strategyContext()->searchMessageStrategy); if(!_unavailable) initiateStrategy(); return true; } bool ImapService::Source::prepareMessages(const QList > &messageIds) { if (!_service->_client) { _service->errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to prepare")); return false; } QList > unresolved; QSet referringIds; QMailMessageIdList externaliseIds; QList >::const_iterator it = messageIds.begin(), end = messageIds.end(); for ( ; it != end; ++it) { if (!(*it).second.isValid()) { // This message just needs to be externalised externaliseIds.append((*it).first.containingMessageId()); } else { // This message needs to be made available for another message's reference unresolved.append(*it); referringIds.insert((*it).second.containingMessageId()); } } if (!unresolved.isEmpty()) { bool external(false); // Are these messages being resolved for internal or external references? QMailMessageKey key(QMailMessageKey::id(referringIds.toList())); QMailMessageKey::Properties props(QMailMessageKey::Id | QMailMessageKey::ParentAccountId | QMailMessageKey::Status); foreach (const QMailMessageMetaData &metaData, QMailStore::instance()->messagesMetaData(key, props)) { if ((metaData.parentAccountId() != _service->accountId()) || !(metaData.status() & QMailMessage::TransmitFromExternal)) { // This message won't be transmitted by reference from the IMAP server - supply an external reference external = true; break; } } _service->_client->strategyContext()->prepareMessagesStrategy.setUnresolved(unresolved, external); appendStrategy(&_service->_client->strategyContext()->prepareMessagesStrategy, SIGNAL(messagesPrepared(QMailMessageIdList))); } if (!externaliseIds.isEmpty()) { QMailAccountConfiguration accountCfg(_service->accountId()); ImapConfiguration imapCfg(accountCfg); QMailFolderId sentId(QMailAccount(_service->accountId()).standardFolder(QMailFolder::SentFolder)); // Prepare these messages by copying to the sent folder _service->_client->strategyContext()->externalizeMessagesStrategy.clearSelection(); _service->_client->strategyContext()->externalizeMessagesStrategy.appendMessageSet(externaliseIds, sentId); appendStrategy(&_service->_client->strategyContext()->externalizeMessagesStrategy, SIGNAL(messagesPrepared(QMailMessageIdList))); } if(!_unavailable) return initiateStrategy(); return true; } bool ImapService::Source::setStrategy(ImapStrategy *strategy, const char *signal) { disconnect(this, SIGNAL(messageActionCompleted(QMailMessageIdList))); if (signal) { connect(this, SIGNAL(messageActionCompleted(QMailMessageIdList)), this, signal); } resetExpiryTimer(); _unavailable = true; _service->_client->setStrategy(strategy); _service->_client->newConnection(); return true; } void ImapService::Source::appendStrategy(ImapStrategy *strategy, const char *signal) { _pendingStrategies.append(qMakePair(strategy, QLatin1String(signal))); } bool ImapService::Source::initiateStrategy() { if (_pendingStrategies.isEmpty()) return false; QPair data(_pendingStrategies.takeFirst()); return setStrategy(data.first, data.second.latin1()); } // Copy or Move Completed void ImapService::Source::messageCopyCompleted(QMailMessage &message, const QMailMessage &original) { if (_service->_client->strategy()->error()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("Destination message failed to match source message")); return; } if (_setMask || _unsetMask) { if (_setMask) { message.setStatus(_setMask, true); } if (_unsetMask) { message.setStatus(_unsetMask, false); } } Q_UNUSED(original); } void ImapService::Source::messageActionCompleted(const QString &uid) { if (uid.startsWith("id:")) { emit messageActionCompleted(QMailMessageIdList() << QMailMessageId(uid.mid(3).toULongLong())); } else if (!uid.isEmpty()) { QMailMessageMetaData metaData(uid, _service->accountId()); if (metaData.id().isValid()) { emit messageActionCompleted(QMailMessageIdList() << metaData.id()); } } } void ImapService::Source::retrievalCompleted() { _strategyExpiryTimer.stop(); _unavailable = false; _setMask = 0; _unsetMask = 0; // See if there are any other actions pending if (initiateStrategy()) { return; } if (_queuedMailCheckInProgress) { if (_mailCheckPhase == RetrieveFolders) { _mailCheckPhase = RetrieveMessages; bool accountCheck = false; QMailFolderIdList folders; if (!_mailCheckFolderId.isValid()) { // Full check all folders accountCheck = true; } else if (_queuedFoldersFullCheck.contains(_mailCheckFolderId)) { // Full check only _mailCheckFolderId accountCheck = true; folders.append(_mailCheckFolderId); } else { // Retrieve only new mail in _mailCheckFolderId folders.append(_mailCheckFolderId); } _queuedFoldersFullCheck.removeAll(_mailCheckFolderId); retrieveMessageLists(_service->accountId(), folders, 1, QMailMessageSortKey(), accountCheck); return; } else { // Push email must be established _service->_establishingPushEmail = false; _service->_pushRetry = ThirtySeconds; _queuedMailCheckInProgress = false; emit _service->availabilityChanged(true); } } else { emit _service->actionCompleted(true); } if (_synchronizing) { _synchronizing = false; // Mark this account as synchronized QMailAccount account(_service->accountId()); if (!(account.status() & QMailAccount::Synchronized)) { account.setStatus(QMailAccount::Synchronized, true); QMailStore::instance()->updateAccount(&account); } } if (!_queuedFolders.isEmpty()) { queueMailCheck(_queuedFolders.first()); } } // Interval mail checking timer has expired, perform mail check on all folders void ImapService::Source::intervalCheck() { _service->_client->requestRapidClose(); exportUpdates(_service->accountId()); // Convenient for user to export pending changes also queueMailCheck(QMailFolderId()); // Full check including flags } // Push interval mail checking timer has expired, perform mail check on all push enabled folders void ImapService::Source::pushIntervalCheck() { _service->_client->requestRapidClose(); exportUpdates(_service->accountId()); // Convenient for user to export pending changes also QMailFolderIdList ids(_service->_client->configurationIdleFolderIds()); if (ids.count()) { foreach(QMailFolderId id, ids) { // Check for flag changes and new mail _service->_source->queueFlagsChangedCheck(id); } } } void ImapService::Source::queueMailCheck(QMailFolderId folderId) { if (_unavailable) { if (!_queuedFolders.contains(folderId)) { _queuedFolders.append(folderId); } return; } _queuedFolders.removeAll(folderId); _queuedMailCheckInProgress = true; _mailCheckPhase = RetrieveFolders; _mailCheckFolderId = folderId; emit _service->availabilityChanged(false); _service->_client->requestRapidClose(); if (folderId.isValid()) { retrievalCompleted(); // move onto retrieveMessageList stage } else { retrieveFolderList(_service->accountId(), folderId, true); } } void ImapService::Source::queueFlagsChangedCheck(QMailFolderId folderId) { if (!_queuedFoldersFullCheck.contains(folderId)) { _queuedFoldersFullCheck.append(folderId); } queueMailCheck(folderId); } void ImapService::Source::retrievalTerminated() { _strategyExpiryTimer.stop(); _unavailable = false; _synchronizing = false; if (_queuedMailCheckInProgress) { _queuedMailCheckInProgress = false; emit _service->availabilityChanged(true); } // Just give up if an error occurs _queuedFolders.clear(); _queuedFoldersFullCheck.clear(); } void ImapService::Source::resetExpiryTimer() { static const int ExpirySeconds = 180; // Should be larger than imapservice.h value _strategyExpiryTimer.start(ExpirySeconds * 1000); } void ImapService::Source::expireStrategy() { qMailLog(Messaging) << "IMAP Strategy is not progressing. Internally reseting IMAP service for account" << _service->_accountId; _service->disable(); _service->enable(); } void ImapService::Source::emitActionSuccessfullyCompleted() { _service->actionCompleted(true); } ImapService::ImapService(const QMailAccountId &accountId) : QMailMessageService(), _accountId(accountId), _client(0), _source(new Source(this)), _restartPushEmailTimer(new QTimer(this)), _accountWasEnabled(false), _accountWasPushEnabled(false) { QMailAccount account(accountId); if (!(account.status() & QMailAccount::CanSearchOnServer)) { account.setStatus(QMailAccount::CanSearchOnServer, true); if (!QMailStore::instance()->updateAccount(&account)) { qWarning() << "Unable to update account" << account.id() << "to set imap CanSearchOnServer"; } } if (account.status() & QMailAccount::Enabled) { enable(); } connect(_restartPushEmailTimer, SIGNAL(timeout()), this, SLOT(restartPushEmail())); connect(QMailStore::instance(), SIGNAL(accountsUpdated(const QMailAccountIdList&)), this, SLOT(accountsUpdated(const QMailAccountIdList&))); } void ImapService::enable() { _accountWasEnabled = true; _client = new ImapClient(this); _source->initClientConnections(); _client->setAccount(_accountId); _establishingPushEmail = false; _pushRetry = ThirtySeconds; connect(_client, SIGNAL(progressChanged(uint, uint)), this, SIGNAL(progressChanged(uint, uint))); connect(_client, SIGNAL(progressChanged(uint, uint)), _source, SLOT(resetExpiryTimer())); connect(_client, SIGNAL(errorOccurred(int, QString)), this, SLOT(errorOccurred(int, QString))); connect(_client, SIGNAL(errorOccurred(QMailServiceAction::Status::ErrorCode, QString)), this, SLOT(errorOccurred(QMailServiceAction::Status::ErrorCode, QString))); connect(_client, SIGNAL(updateStatus(QString)), this, SLOT(updateStatus(QString))); connect(_client, SIGNAL(restartPushEmail()), this, SLOT(restartPushEmail())); QMailAccountConfiguration accountCfg(_accountId); ImapConfiguration imapCfg(accountCfg); _accountWasPushEnabled = imapCfg.pushEnabled(); _previousPushFolders = imapCfg.pushFolders(); if (imapCfg.pushEnabled()) { initiatePushEmail(); } _source->setIntervalTimer(imapCfg.checkInterval()); } void ImapService::disable() { QMailAccountConfiguration accountCfg(_accountId); ImapConfiguration imapCfg(accountCfg); _accountWasEnabled = false; _accountWasPushEnabled = imapCfg.pushEnabled(); _previousPushFolders = imapCfg.pushFolders(); _restartPushEmailTimer->stop(); _source->setIntervalTimer(0); _source->setPushIntervalTimer(0); _source->retrievalTerminated(); delete _client; _client = 0; } void ImapService::accountsUpdated(const QMailAccountIdList &ids) { if (!ids.contains(_accountId)) return; QMailAccount account(_accountId); QMailAccountConfiguration accountCfg(_accountId); ImapConfiguration imapCfg(accountCfg); bool isEnabled(account.status() & QMailAccount::Enabled); bool isPushEnabled(imapCfg.pushEnabled()); QStringList pushFolders(imapCfg.pushFolders()); if (!isEnabled) { if (_accountWasEnabled) { // Account changed from enabled to disabled cancelOperation(QMailServiceAction::Status::ErrConfiguration, tr("Account disabled")); disable(); } // Account is disabled nothing more todo return; } if ((_accountWasPushEnabled != isPushEnabled) || (_previousPushFolders != pushFolders)) { // push email settings have changed, restart client if (_accountWasEnabled) { disable(); } enable(); } else if (!_accountWasEnabled) { // account changed from disabled to enabled enable(); } // account was enabled and still is, update checkinterval // in case it changed _source->setIntervalTimer(imapCfg.checkInterval()); } ImapService::~ImapService() { delete _source; } QString ImapService::service() const { return serviceKey; } QMailAccountId ImapService::accountId() const { return _accountId; } bool ImapService::hasSource() const { return true; } QMailMessageSource &ImapService::source() const { return *_source; } bool ImapService::available() const { return true; } bool ImapService::cancelOperation(QMailServiceAction::Status::ErrorCode code, const QString &text) { if (!_client) { errorOccurred(QMailServiceAction::Status::ErrFrameworkFault, tr("Account disabled")); return false; } _client->cancelTransfer(code, text); _client->closeConnection(); _source->retrievalTerminated(); return true; } void ImapService::restartPushEmail() { cancelOperation(QMailServiceAction::Status::ErrInternalStateReset, tr("Initiating push email")); initiatePushEmail(); } void ImapService::initiatePushEmail() { _restartPushEmailTimer->stop(); QMailFolderIdList ids(_client->configurationIdleFolderIds()); if (ids.count()) { _establishingPushEmail = true; foreach(QMailFolderId id, ids) { // Check for flag changes and new mail _source->queueFlagsChangedCheck(id); } } // Interval check to update flags on servers that do not push flag changes when idling e.g. gmail _source->setPushIntervalTimer(60); // minutes } bool ImapService::pushEmailEstablished() { if (!_establishingPushEmail) return true; if (_client->idlesEstablished()) return true; const int oneHour = 60*60; qMailLog(Messaging) << "Push email connection could not be established. Reattempting to establish in" << _pushRetry << "seconds"; _restartPushEmailTimer->start(_pushRetry*1000); _pushRetry = qMin(oneHour, _pushRetry * 2); return false; } void ImapService::errorOccurred(int code, const QString &text) { if (!pushEmailEstablished()) return; _source->retrievalTerminated(); updateStatus(code, text, _accountId); emit actionCompleted(false); } void ImapService::errorOccurred(QMailServiceAction::Status::ErrorCode code, const QString &text) { if (!pushEmailEstablished()) return; _source->retrievalTerminated(); updateStatus(code, text, _accountId); emit actionCompleted(false); } void ImapService::updateStatus(const QString &text) { updateStatus(QMailServiceAction::Status::ErrNoError, text, _accountId); } class ImapConfigurator : public QMailMessageServiceConfigurator { public: ImapConfigurator(); ~ImapConfigurator(); virtual QString service() const; virtual QString displayName() const; #ifndef QMF_NO_MESSAGE_SERVICE_EDITOR virtual QMailMessageServiceEditor *createEditor(QMailMessageServiceFactory::ServiceType type); #endif }; ImapConfigurator::ImapConfigurator() { } ImapConfigurator::~ImapConfigurator() { } QString ImapConfigurator::service() const { return serviceKey; } QString ImapConfigurator::displayName() const { return QCoreApplication::instance()->translate("QMailMessageService", "IMAP"); } #ifndef QMF_NO_MESSAGE_SERVICE_EDITOR QMailMessageServiceEditor *ImapConfigurator::createEditor(QMailMessageServiceFactory::ServiceType type) { if (type == QMailMessageServiceFactory::Source) return new ImapSettings; return 0; } #endif Q_EXPORT_PLUGIN2(imap,ImapServicePlugin) ImapServicePlugin::ImapServicePlugin() : QMailMessageServicePlugin() { } QString ImapServicePlugin::key() const { return serviceKey; } bool ImapServicePlugin::supports(QMailMessageServiceFactory::ServiceType type) const { return (type == QMailMessageServiceFactory::Source); } bool ImapServicePlugin::supports(QMailMessage::MessageType type) const { return (type == QMailMessage::Email); } QMailMessageService *ImapServicePlugin::createService(const QMailAccountId &id) { return new ImapService(id); } QMailMessageServiceConfigurator *ImapServicePlugin::createServiceConfigurator() { return new ImapConfigurator(); } #include "imapservice.moc"