/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Messaging Framework. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "popservice.h" #include "popconfiguration.h" #ifndef QMF_NO_WIDGETS #include "popsettings.h" #endif #include #include #include #include namespace { const QString serviceKey("pop3"); } class PopService::Source : public QMailMessageSource { Q_OBJECT public: Source(PopService *service) : QMailMessageSource(service), _service(service), _deleting(false), _unavailable(false), _mailCheckQueued(false), _queuedMailCheckInProgress(false) { connect(&_service->_client, SIGNAL(allMessagesReceived()), this, SIGNAL(newMessagesAvailable())); connect(&_service->_client, SIGNAL(messageActionCompleted(QString)), this, SLOT(messageActionCompleted(QString))); connect(&_service->_client, SIGNAL(retrievalCompleted()), this, SLOT(retrievalCompleted())); connect(&_intervalTimer, SIGNAL(timeout()), this, SLOT(queueMailCheck())); } void setIntervalTimer(int interval) { _intervalTimer.stop(); if (interval > 0) _intervalTimer.start(interval*1000*60); // interval minutes } QMailStore::MessageRemovalOption messageRemovalOption() const override { return QMailStore::CreateRemovalRecord; } public slots: bool retrieveFolderList(const QMailAccountId &accountId, const QMailFolderId &folderId, bool descending) override; bool retrieveMessageList(const QMailAccountId &accountId, const QMailFolderId &folderId, uint minimum, const QMailMessageSortKey &sort) override; bool retrieveMessageLists(const QMailAccountId &accountId, const QMailFolderIdList &folderIds, uint minimum, const QMailMessageSortKey &sort) override; bool retrieveMessages(const QMailMessageIdList &messageIds, QMailRetrievalAction::RetrievalSpecification spec) override; bool retrieveAll(const QMailAccountId &accountId) override; bool exportUpdates(const QMailAccountId &accountId) override; bool synchronize(const QMailAccountId &accountId) override; bool deleteMessages(const QMailMessageIdList &ids) override; void messageActionCompleted(const QString &uid); void retrievalCompleted(); void retrievalTerminated(); void queueMailCheck(); private: PopService *_service; bool _deleting; bool _unavailable; bool _mailCheckQueued; bool _queuedMailCheckInProgress; QTimer _intervalTimer; }; bool PopService::Source::retrieveFolderList(const QMailAccountId &accountId, const QMailFolderId &folderId, bool descending) { if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } if (!_service->_client.findInbox()) { // find/create local inbox // Inbox created so first sync, so test connection _service->_client.testConnection(); _unavailable = true; } else { // Just report success _service->updateStatus(""); QTimer::singleShot(0, this, SLOT(retrievalCompleted())); } return true; Q_UNUSED(descending) Q_UNUSED(folderId) } bool PopService::Source::retrieveMessageLists(const QMailAccountId &accountId, const QMailFolderIdList &folderIds, uint minimum, const QMailMessageSortKey &sort) { if (folderIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No folders specified")); return false; } return retrieveMessageList(accountId, QMailFolderId(), minimum, sort); } bool PopService::Source::retrieveMessageList(const QMailAccountId &accountId, const QMailFolderId &folderId, uint minimum, const QMailMessageSortKey &sort) { if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } QMailMessageKey countKey(QMailMessageKey::parentAccountId(accountId)); countKey &= ~QMailMessageKey::status(QMailMessage::Temporary); uint existing = QMailStore::instance()->countMessages(countKey); existing = qMin(existing, minimum); _service->_client.setOperation(QMailRetrievalAction::Auto); _service->_client.setAdditional(minimum - existing); if (!_service->_client.synchronizationEnabled(folderId)) { // Just report success _service->updateStatus(""); QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } _service->_client.newConnection(); _unavailable = true; return true; Q_UNUSED(folderId) Q_UNUSED(sort) } bool PopService::Source::retrieveMessages(const QMailMessageIdList &messageIds, QMailRetrievalAction::RetrievalSpecification spec) { if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to retrieve")); return false; } if (spec == QMailRetrievalAction::Flags) { // Just report success QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } SelectionMap selectionMap; foreach (const QMailMessageId& id, messageIds) { QMailMessageMetaData message(id); selectionMap.insert(message.serverUid(), id); } _service->_client.setOperation(spec); _service->_client.setSelectedMails(selectionMap); _service->_client.newConnection(); _unavailable = true; return true; } bool PopService::Source::retrieveAll(const QMailAccountId &accountId) { if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } _service->_client.setOperation(QMailRetrievalAction::MetaData); _service->_client.newConnection(); _unavailable = true; return true; } bool PopService::Source::exportUpdates(const QMailAccountId &accountId) { if (!accountId.isValid()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No account specified")); return false; } // Just report success QTimer::singleShot(0, this, SLOT(retrievalCompleted())); return true; } bool PopService::Source::synchronize(const QMailAccountId &accountId) { return retrieveAll(accountId); } bool PopService::Source::deleteMessages(const QMailMessageIdList &messageIds) { if (messageIds.isEmpty()) { _service->errorOccurred(QMailServiceAction::Status::ErrInvalidData, tr("No messages to delete")); return false; } QMailAccountConfiguration accountCfg(_service->accountId()); PopConfiguration popCfg(accountCfg); if (popCfg.canDeleteMail()) { // Delete the messages from the server SelectionMap selectionMap; foreach (const QMailMessageId& id, messageIds) { QMailMessageMetaData message(id); selectionMap.insert(message.serverUid(), id); } _deleting = true; _service->_client.setDeleteOperation(); _service->_client.setSelectedMails(selectionMap); _service->_client.newConnection(); _unavailable = true; return true; } // Just delete the local copies return QMailMessageSource::deleteMessages(messageIds); } void PopService::Source::messageActionCompleted(const QString &uid) { if (_deleting) { QMailMessageMetaData metaData(uid, _service->accountId()); if (metaData.id().isValid()) { QMailMessageIdList messageIds; messageIds.append(metaData.id()); emit messagesDeleted(messageIds); } } } void PopService::Source::retrievalCompleted() { _unavailable = false; if (_queuedMailCheckInProgress) { _queuedMailCheckInProgress = false; emit _service->availabilityChanged(true); } emit _service->actionCompleted(true); _deleting = false; if (_mailCheckQueued) { queueMailCheck(); } } void PopService::Source::queueMailCheck() { if (_unavailable) { _mailCheckQueued = true; return; } _mailCheckQueued = false; _queuedMailCheckInProgress = true; emit _service->availabilityChanged(false); synchronize(_service->accountId()); } void PopService::Source::retrievalTerminated() { _unavailable = false; if (_queuedMailCheckInProgress) { _queuedMailCheckInProgress = false; emit _service->availabilityChanged(true); } // Just give up if an error occurs _mailCheckQueued = false; } PopService::PopService(const QMailAccountId &accountId) : QMailMessageService(), _client(accountId, this), _source(new Source(this)) { connect(&_client, SIGNAL(progressChanged(uint, uint)), this, SIGNAL(progressChanged(uint, uint))); 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(QMailStore::instance(), SIGNAL(accountsUpdated(const QMailAccountIdList&)), this, SLOT(accountsUpdated(const QMailAccountIdList&))); QMailAccountConfiguration accountCfg(accountId); PopConfiguration popCfg(accountCfg); _source->setIntervalTimer(popCfg.checkInterval()); } PopService::~PopService() { delete _source; } QString PopService::service() const { return serviceKey; } QMailAccountId PopService::accountId() const { return _client.accountId(); } bool PopService::hasSource() const { return true; } QMailMessageSource &PopService::source() const { return *_source; } bool PopService::available() const { return true; } bool PopService::cancelOperation(QMailServiceAction::Status::ErrorCode code, const QString &text) { _client.cancelTransfer(code, text); _client.closeConnection(); _source->retrievalTerminated(); return true; } void PopService::errorOccurred(int code, const QString &text) { updateStatus(code, text, _client.accountId()); _source->retrievalTerminated(); emit actionCompleted(false); } void PopService::errorOccurred(QMailServiceAction::Status::ErrorCode code, const QString &text) { updateStatus(code, text, _client.accountId()); _source->retrievalTerminated(); emit actionCompleted(false); } void PopService::updateStatus(const QString &text) { updateStatus(QMailServiceAction::Status::ErrNoError, text, _client.accountId()); } void PopService::accountsUpdated(const QMailAccountIdList &ids) { if (!ids.contains(accountId())) return; QMailAccount account(accountId()); bool isEnabled(account.status() & QMailAccount::Enabled); if (!isEnabled) { // cancel any mail check in progress cancelOperation(QMailServiceAction::Status::ErrConfiguration, tr("Account disabled")); return; } // keep the check interval up to date QMailAccountConfiguration accountCfg(accountId()); PopConfiguration popCfg(accountCfg); _source->setIntervalTimer(popCfg.checkInterval()); } class PopConfigurator : public QMailMessageServiceConfigurator { public: PopConfigurator(); ~PopConfigurator(); QString service() const override; QString displayName() const override; #ifndef QMF_NO_WIDGETS QMailMessageServiceEditor *createEditor(QMailMessageServiceFactory::ServiceType type) override; #endif }; PopConfigurator::PopConfigurator() { } PopConfigurator::~PopConfigurator() { } QString PopConfigurator::service() const { return serviceKey; } QString PopConfigurator::displayName() const { return QCoreApplication::instance()->translate("QMailMessageService", "POP"); } #ifndef QMF_NO_WIDGETS QMailMessageServiceEditor *PopConfigurator::createEditor(QMailMessageServiceFactory::ServiceType type) { if (type == QMailMessageServiceFactory::Source) return new PopSettings; return 0; } #endif PopServicePlugin::PopServicePlugin() : QMailMessageServicePlugin() { } QString PopServicePlugin::key() const { return serviceKey; } bool PopServicePlugin::supports(QMailMessageServiceFactory::ServiceType type) const { return (type == QMailMessageServiceFactory::Source); } bool PopServicePlugin::supports(QMailMessage::MessageType type) const { return (type == QMailMessage::Email); } QMailMessageService *PopServicePlugin::createService(const QMailAccountId &id) { return new PopService(id); } QMailMessageServiceConfigurator *PopServicePlugin::createServiceConfigurator() { return new PopConfigurator(); } #include "popservice.moc"