summaryrefslogtreecommitdiffstats
path: root/examples/imapresource/imapserverproxy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/imapresource/imapserverproxy.cpp')
-rw-r--r--examples/imapresource/imapserverproxy.cpp88
1 files changed, 74 insertions, 14 deletions
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp
index 0cc43b8..538105c 100644
--- a/examples/imapresource/imapserverproxy.cpp
+++ b/examples/imapresource/imapserverproxy.cpp
@@ -37,8 +37,6 @@
37#include "log.h" 37#include "log.h"
38#include "test.h" 38#include "test.h"
39 39
40SINK_DEBUG_AREA("imapserverproxy")
41
42using namespace Imap; 40using namespace Imap;
43 41
44const char* Imap::Flags::Seen = "\\Seen"; 42const char* Imap::Flags::Seen = "\\Seen";
@@ -57,6 +55,7 @@ const char* Imap::FolderFlags::Trash = "\\Trash";
57const char* Imap::FolderFlags::Archive = "\\Archive"; 55const char* Imap::FolderFlags::Archive = "\\Archive";
58const char* Imap::FolderFlags::Junk = "\\Junk"; 56const char* Imap::FolderFlags::Junk = "\\Junk";
59const char* Imap::FolderFlags::Flagged = "\\Flagged"; 57const char* Imap::FolderFlags::Flagged = "\\Flagged";
58const char* Imap::FolderFlags::Drafts = "\\Drafts";
60 59
61const char* Imap::Capabilities::Namespace = "NAMESPACE"; 60const char* Imap::Capabilities::Namespace = "NAMESPACE";
62const char* Imap::Capabilities::Uidplus = "UIDPLUS"; 61const char* Imap::Capabilities::Uidplus = "UIDPLUS";
@@ -98,17 +97,25 @@ static KAsync::Job<void> runJob(KJob *job)
98 }); 97 });
99} 98}
100 99
101ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSession(new KIMAP2::Session(serverUrl, qint16(port))), mSessionCache(sessionCache) 100KIMAP2::Session *createNewSession(const QString &serverUrl, int port)
102{ 101{
103 QObject::connect(mSession, &KIMAP2::Session::sslErrors, [this](const QList<QSslError> &errors) { 102 auto newSession = new KIMAP2::Session(serverUrl, qint16(port));
103 if (Sink::Test::testModeEnabled()) {
104 newSession->setTimeout(1);
105 } else {
106 newSession->setTimeout(40);
107 }
108 QObject::connect(newSession, &KIMAP2::Session::sslErrors, [=](const QList<QSslError> &errors) {
104 SinkLog() << "Received ssl error: " << errors; 109 SinkLog() << "Received ssl error: " << errors;
105 mSession->ignoreErrors(errors); 110 newSession->ignoreErrors(errors);
106 }); 111 });
112 return newSession;
113}
107 114
108 if (Sink::Test::testModeEnabled()) { 115ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache) : mSessionCache(sessionCache), mSession(nullptr)
109 mSession->setTimeout(1); 116{
110 } else { 117 if (!mSessionCache || mSessionCache->isEmpty()) {
111 mSession->setTimeout(40); 118 mSession = createNewSession(serverUrl, port);
112 } 119 }
113} 120}
114 121
@@ -161,12 +168,16 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString
161 // SinkTrace() << "Found user namespaces: " << mNamespaces.user; 168 // SinkTrace() << "Found user namespaces: " << mNamespaces.user;
162 }).then([=] (const KAsync::Error &error) { 169 }).then([=] (const KAsync::Error &error) {
163 if (error) { 170 if (error) {
164 if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT) { 171 switch (error.errorCode) {
172 case KIMAP2::LoginJob::ErrorCode::ERR_HOST_NOT_FOUND:
173 return KAsync::error(HostNotFoundError, "Host not found: " + error.errorMessage);
174 case KIMAP2::LoginJob::ErrorCode::ERR_COULD_NOT_CONNECT:
165 return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage); 175 return KAsync::error(CouldNotConnectError, "Failed to connect: " + error.errorMessage);
166 } else if (error.errorCode == KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED) { 176 case KIMAP2::LoginJob::ErrorCode::ERR_SSL_HANDSHAKE_FAILED:
167 return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage); 177 return KAsync::error(SslHandshakeError, "Ssl handshake failed: " + error.errorMessage);
178 default:
179 return KAsync::error(error);
168 } 180 }
169 return KAsync::error(error);
170 } 181 }
171 return KAsync::null(); 182 return KAsync::null();
172 }); 183 });
@@ -188,6 +199,12 @@ KAsync::Job<void> ImapServerProxy::logout()
188 } 199 }
189} 200}
190 201
202bool ImapServerProxy::isGmail() const
203{
204 //Magic capability that only gmail has
205 return mCapabilities.contains("X-GM-EXT-1");
206}
207
191KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) 208KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox)
192{ 209{
193 auto select = new KIMAP2::SelectJob(mSession); 210 auto select = new KIMAP2::SelectJob(mSession);
@@ -297,6 +314,7 @@ KAsync::Job<void> ImapServerProxy::fetch(const KIMAP2::ImapSet &set, KIMAP2::Fet
297 fetch->setSequenceSet(set); 314 fetch->setSequenceSet(set);
298 fetch->setUidBased(true); 315 fetch->setUidBased(true);
299 fetch->setScope(scope); 316 fetch->setScope(scope);
317 fetch->setAvoidParsing(true);
300 QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback); 318 QObject::connect(fetch, &KIMAP2::FetchJob::resultReceived, callback);
301 return runJob(fetch); 319 return runJob(fetch);
302} 320}
@@ -437,18 +455,60 @@ QString ImapServerProxy::getNamespace(const QString &name)
437 return ns.name; 455 return ns.name;
438} 456}
439 457
458static bool caseInsensitiveContains(const QByteArray &f, const QByteArrayList &list) {
459 return list.contains(f) || list.contains(f.toLower());
460}
461
462bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags)
463{
464 return caseInsensitiveContains(f, flags);
465}
466
467static void reportFolder(const Folder &f, QSharedPointer<QSet<QString>> reportedList, std::function<void(const Folder &)> callback) {
468 if (!reportedList->contains(f.path())) {
469 reportedList->insert(f.path());
470 auto c = f;
471 c.noselect = true;
472 callback(c);
473 if (!f.parentPath().isEmpty()){
474 reportFolder(f.parentFolder(), reportedList, callback);
475 }
476 }
477}
478
440KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) 479KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback)
441{ 480{
442 SinkTrace() << "Fetching folders"; 481 SinkTrace() << "Fetching folders";
443 auto subscribedList = QSharedPointer<QSet<QString>>::create() ; 482 auto subscribedList = QSharedPointer<QSet<QString>>::create() ;
483 auto reportedList = QSharedPointer<QSet<QString>>::create() ;
444 return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){ 484 return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){
445 *subscribedList << mailbox.name; 485 *subscribedList << mailbox.name;
446 }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) { 486 }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) {
447 bool noselect = flags.contains(QByteArray(FolderFlags::Noselect).toLower()) || flags.contains(QByteArray(FolderFlags::Noselect)); 487 bool noselect = caseInsensitiveContains(FolderFlags::Noselect, flags);
448 bool subscribed = subscribedList->contains(mailbox.name); 488 bool subscribed = subscribedList->contains(mailbox.name);
489 if (isGmail()) {
490 bool inbox = mailbox.name.toLower() == "inbox";
491 bool sent = caseInsensitiveContains(FolderFlags::Sent, flags);
492 bool drafts = caseInsensitiveContains(FolderFlags::Drafts, flags);
493 bool trash = caseInsensitiveContains(FolderFlags::Trash, flags);
494 /**
495 * Because gmail duplicates messages all over the place we only support a few selected folders for now that should be mostly exclusive.
496 */
497 if (!(inbox || sent || drafts || trash)) {
498 return;
499 }
500 }
449 SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; 501 SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed;
450 auto ns = getNamespace(mailbox.name); 502 auto ns = getNamespace(mailbox.name);
451 callback(Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}); 503 auto folder = Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags};
504
505 //call callback for parents if that didn't already happen.
506 //This is necessary because we can have missing bits in the hierarchy in IMAP, but this will not work in sink because we'd end up with an incomplete tree.
507 if (!folder.parentPath().isEmpty() && !reportedList->contains(folder.parentPath())) {
508 reportFolder(folder.parentFolder(), reportedList, callback);
509 }
510 reportedList->insert(folder.path());
511 callback(folder);
452 })); 512 }));
453} 513}
454 514