/* * Copyright (C) 2015 Christian Mollekopf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "imapserverproxy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" using namespace Imap; const char* Imap::Flags::Seen = "\\Seen"; const char* Imap::Flags::Deleted = "\\Deleted"; const char* Imap::Flags::Answered = "\\Answered"; const char* Imap::Flags::Flagged = "\\Flagged"; static KAsync::Job runJob(KJob *job) { return KAsync::start([job](KAsync::Future &future) { QObject::connect(job, &KJob::result, [&future](KJob *job) { if (job->error()) { Warning() << "Job failed: " << job->errorString(); future.setError(job->error(), job->errorString()); } else { future.setFinished(); } }); job->start(); }); } class SessionUiProxy : public KIMAP::SessionUiProxy { public: bool ignoreSslError( const KSslErrorUiData &errorData ) { return true; } }; ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port) : mSession(new KIMAP::Session(serverUrl, port)) { mSession->setUiProxy(SessionUiProxy::Ptr(new SessionUiProxy)); mSession->setTimeout(10); } KAsync::Job ImapServerProxy::login(const QString &username, const QString &password) { auto loginJob = new KIMAP::LoginJob(mSession); loginJob->setUserName(username); loginJob->setPassword(password); loginJob->setAuthenticationMode(KIMAP::LoginJob::Plain); loginJob->setEncryptionMode(KIMAP::LoginJob::EncryptionMode::AnySslVersion); return runJob(loginJob); } KAsync::Job ImapServerProxy::select(const QString &mailbox) { auto select = new KIMAP::SelectJob(mSession); select->setMailBox(mailbox); // select->setCondstoreEnabled(serverSupportsCondstore()); return runJob(select); } KAsync::Job ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList &flags, const QDateTime &internalDate) { auto append = new KIMAP::AppendJob(mSession); append->setMailBox(mailbox); append->setContent(content); append->setFlags(flags); append->setInternalDate(internalDate); return runJob(append); } KAsync::Job ImapServerProxy::store(const KIMAP::ImapSet &set, const QList &flags) { auto store = new KIMAP::StoreJob(mSession); store->setUidBased(true); store->setSequenceSet(set); store->setFlags(flags); store->setMode(KIMAP::StoreJob::AppendFlags); return runJob(store); } KAsync::Job ImapServerProxy::create(const QString &mailbox) { auto create = new KIMAP::CreateJob(mSession); create->setMailBox(mailbox); return runJob(create); } KAsync::Job ImapServerProxy::rename(const QString &mailbox, const QString &newMailbox) { auto rename = new KIMAP::RenameJob(mSession); rename->setSourceMailBox(mailbox); rename->setDestinationMailBox(newMailbox); return runJob(rename); } KAsync::Job ImapServerProxy::remove(const QString &mailbox) { auto job = new KIMAP::DeleteJob(mSession); job->setMailBox(mailbox); return runJob(job); } KAsync::Job ImapServerProxy::expunge() { auto job = new KIMAP::ExpungeJob(mSession); return runJob(job); } KAsync::Job ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::FetchJob::FetchScope scope, FetchCallback callback) { auto fetch = new KIMAP::FetchJob(mSession); fetch->setSequenceSet(set); fetch->setUidBased(true); fetch->setScope(scope); QObject::connect(fetch, static_cast &, const QMap &, const QMap &, const QMap &, const QMap &)>(&KIMAP::FetchJob::headersReceived), callback); return runJob(fetch); } KAsync::Job ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::FetchJob::FetchScope scope, const std::function &)> &callback) { return fetch(set, scope, [callback](const QString &mailbox, const QMap &uids, const QMap &sizes, const QMap &attrs, const QMap &flags, const QMap &messages) { QVector list; for (const auto &id : uids.keys()) { list << Message{uids.value(id), sizes.value(id), attrs.value(id), flags.value(id), messages.value(id)}; } callback(list); }); } KAsync::Job> ImapServerProxy::fetchHeaders(const QString &mailbox) { auto list = QSharedPointer>::create(); KIMAP::FetchJob::FetchScope scope; scope.parts.clear(); scope.mode = KIMAP::FetchJob::FetchScope::Headers; //Fetch headers of all messages return fetch(KIMAP::ImapSet(1, 0), scope, [list](const QString &mailbox, const QMap &uids, const QMap &sizes, const QMap &attrs, const QMap &flags, const QMap &messages) { Trace() << "Received " << uids.size() << " headers from " << mailbox; Trace() << uids.size() << sizes.size() << attrs.size() << flags.size() << messages.size(); //TODO based on the data available here, figure out which messages to actually fetch //(we only fetched headers and structure so far) //We could i.e. build chunks to fetch based on the size for (const auto &id : uids.keys()) { list->append(uids.value(id)); } }) .then>([list](){ return *list; }); } KAsync::Job ImapServerProxy::list(KIMAP::ListJob::Option option, const std::function &mailboxes,const QList > &flags)> &callback) { auto listJob = new KIMAP::ListJob(mSession); listJob->setOption(option); // listJob->setQueriedNamespaces(serverNamespaces()); QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived, listJob, callback); //Figure out the separator character on the first list issued. if (mSeparatorCharacter.isNull()) { QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived, listJob, [this](const QList &mailboxes,const QList > &flags) { if (!mailboxes.isEmpty() && mSeparatorCharacter.isNull()) { mSeparatorCharacter = mailboxes.first().separator; } } ); } return runJob(listJob); } KAsync::Job ImapServerProxy::remove(const QString &mailbox, const QByteArray &imapSet) { const auto set = KIMAP::ImapSet::fromImapSequenceSet(imapSet); return select(mailbox).then(store(set, QByteArrayList() << Flags::Deleted)).then(expunge()); } KAsync::Job ImapServerProxy::fetchFolders(std::function &)> callback) { Trace() << "Fetching folders"; return list(KIMAP::ListJob::IncludeUnsubscribed, [callback](const QList &mailboxes, const QList > &flags){ QVector list; for (const auto &mailbox : mailboxes) { Trace() << "Found mailbox: " << mailbox.name; list << Folder{mailbox.name.split(mailbox.separator)}; } callback(list); }); } KAsync::Job ImapServerProxy::fetchMessages(const Folder &folder, std::function &)> callback) { Q_ASSERT(!mSeparatorCharacter.isNull()); return select(folder.pathParts.join(mSeparatorCharacter)).then>([this, callback, folder]() -> KAsync::Job { return fetchHeaders(folder.pathParts.join(mSeparatorCharacter)).then, QList>([this, callback](const QList &uidsToFetch){ Trace() << "Uids to fetch: " << uidsToFetch; if (uidsToFetch.isEmpty()) { Trace() << "Nothing to fetch"; callback(QVector()); return KAsync::null(); } KIMAP::FetchJob::FetchScope scope; scope.parts.clear(); scope.mode = KIMAP::FetchJob::FetchScope::Full; KIMAP::ImapSet set; set.add(uidsToFetch.toVector()); return fetch(set, scope, callback); }); }); }