summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-04-21 12:06:49 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-04-21 12:07:39 +0200
commit8d20128618b427270c6c7db49a1716f65b8ff840 (patch)
tree1329134c8166040d6719aaa6a5f542dc67f578fc
parentac73ca1d2a23d3b62cca20545e019355f9d00035 (diff)
downloadsink-8d20128618b427270c6c7db49a1716f65b8ff840.tar.gz
sink-8d20128618b427270c6c7db49a1716f65b8ff840.zip
Gmail support.
For the time being we hardcode a list of folders that we synchronize that we know are not duplicating messages.
-rw-r--r--examples/imapresource/imapresource.cpp36
-rw-r--r--examples/imapresource/imapserverproxy.cpp31
-rw-r--r--examples/imapresource/imapserverproxy.h13
3 files changed, 69 insertions, 11 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
index 0579dae..0b3dcf5 100644
--- a/examples/imapresource/imapresource.cpp
+++ b/examples/imapresource/imapresource.cpp
@@ -87,6 +87,25 @@ static QByteArray parentRid(const Imap::Folder &folder)
87 return folder.parentPath().toUtf8(); 87 return folder.parentPath().toUtf8();
88} 88}
89 89
90static QByteArray getSpecialPurposeType(const QByteArrayList &flags)
91{
92 if (Imap::flagsContain(Imap::FolderFlags::Trash, flags)) {
93 return ApplicationDomain::SpecialPurpose::Mail::trash;
94 }
95 if (Imap::flagsContain(Imap::FolderFlags::Drafts, flags)) {
96 return ApplicationDomain::SpecialPurpose::Mail::drafts;
97 }
98 if (Imap::flagsContain(Imap::FolderFlags::Sent, flags)) {
99 return ApplicationDomain::SpecialPurpose::Mail::sent;
100 }
101 return {};
102}
103
104static bool hasSpecialPurposeFlag(const QByteArrayList &flags)
105{
106 return !getSpecialPurposeType(flags).isEmpty();
107}
108
90 109
91class ImapSynchronizer : public Sink::Synchronizer { 110class ImapSynchronizer : public Sink::Synchronizer {
92 Q_OBJECT 111 Q_OBJECT
@@ -100,7 +119,7 @@ public:
100 QByteArray createFolder(const Imap::Folder &f) 119 QByteArray createFolder(const Imap::Folder &f)
101 { 120 {
102 const auto parentFolderRid = parentRid(f); 121 const auto parentFolderRid = parentRid(f);
103 SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid; 122 SinkTraceCtx(mLogCtx) << "Creating folder: " << f.name() << parentFolderRid << f.flags;
104 123
105 const auto remoteId = folderRid(f); 124 const auto remoteId = folderRid(f);
106 Sink::ApplicationDomain::Folder folder; 125 Sink::ApplicationDomain::Folder folder;
@@ -108,10 +127,17 @@ public:
108 folder.setIcon("folder"); 127 folder.setIcon("folder");
109 folder.setEnabled(f.subscribed); 128 folder.setEnabled(f.subscribed);
110 QHash<QByteArray, Query::Comparator> mergeCriteria; 129 QHash<QByteArray, Query::Comparator> mergeCriteria;
111 if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) { 130 auto specialPurpose = [&] {
112 auto type = SpecialPurpose::getSpecialPurposeType(f.name()); 131 if (hasSpecialPurposeFlag(f.flags)) {
113 folder.setSpecialPurpose(QByteArrayList() << type); 132 return getSpecialPurposeType(f.flags);
114 mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(type, Query::Comparator::Contains)); 133 } else if (SpecialPurpose::isSpecialPurposeFolderName(f.name()) && parentFolderRid.isEmpty()) {
134 return SpecialPurpose::getSpecialPurposeType(f.name());
135 }
136 return QByteArray{};
137 }();
138 if (!specialPurpose.isEmpty()) {
139 folder.setSpecialPurpose(QByteArrayList() << specialPurpose);
140 mergeCriteria.insert(ApplicationDomain::Folder::SpecialPurpose::name, Query::Comparator(specialPurpose, Query::Comparator::Contains));
115 } 141 }
116 142
117 if (!parentFolderRid.isEmpty()) { 143 if (!parentFolderRid.isEmpty()) {
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp
index 3305f60..36dbcf5 100644
--- a/examples/imapresource/imapserverproxy.cpp
+++ b/examples/imapresource/imapserverproxy.cpp
@@ -57,6 +57,7 @@ const char* Imap::FolderFlags::Trash = "\\Trash";
57const char* Imap::FolderFlags::Archive = "\\Archive"; 57const char* Imap::FolderFlags::Archive = "\\Archive";
58const char* Imap::FolderFlags::Junk = "\\Junk"; 58const char* Imap::FolderFlags::Junk = "\\Junk";
59const char* Imap::FolderFlags::Flagged = "\\Flagged"; 59const char* Imap::FolderFlags::Flagged = "\\Flagged";
60const char* Imap::FolderFlags::Drafts = "\\Drafts";
60 61
61const char* Imap::Capabilities::Namespace = "NAMESPACE"; 62const char* Imap::Capabilities::Namespace = "NAMESPACE";
62const char* Imap::Capabilities::Uidplus = "UIDPLUS"; 63const char* Imap::Capabilities::Uidplus = "UIDPLUS";
@@ -192,6 +193,12 @@ KAsync::Job<void> ImapServerProxy::logout()
192 } 193 }
193} 194}
194 195
196bool ImapServerProxy::isGmail() const
197{
198 //Magic capability that only gmail has
199 return mCapabilities.contains("X-GM-EXT-1");
200}
201
195KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) 202KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox)
196{ 203{
197 auto select = new KIMAP2::SelectJob(mSession); 204 auto select = new KIMAP2::SelectJob(mSession);
@@ -441,6 +448,15 @@ QString ImapServerProxy::getNamespace(const QString &name)
441 return ns.name; 448 return ns.name;
442} 449}
443 450
451static bool caseInsensitiveContains(const QByteArray &f, const QByteArrayList &list) {
452 return list.contains(f) || list.contains(f.toLower());
453}
454
455bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags)
456{
457 return caseInsensitiveContains(f, flags);
458}
459
444KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) 460KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback)
445{ 461{
446 SinkTrace() << "Fetching folders"; 462 SinkTrace() << "Fetching folders";
@@ -448,8 +464,21 @@ KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder
448 return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){ 464 return list(KIMAP2::ListJob::NoOption, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &){
449 *subscribedList << mailbox.name; 465 *subscribedList << mailbox.name;
450 }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) { 466 }).then(list(KIMAP2::ListJob::IncludeUnsubscribed, [=](const KIMAP2::MailBoxDescriptor &mailbox, const QList<QByteArray> &flags) {
451 bool noselect = flags.contains(QByteArray(FolderFlags::Noselect).toLower()) || flags.contains(QByteArray(FolderFlags::Noselect)); 467 bool noselect = caseInsensitiveContains(FolderFlags::Noselect, flags);
452 bool subscribed = subscribedList->contains(mailbox.name); 468 bool subscribed = subscribedList->contains(mailbox.name);
469 if (isGmail()) {
470 bool inbox = mailbox.name.toLower() == "inbox";
471 bool sent = caseInsensitiveContains(FolderFlags::Sent, flags);
472 bool drafts = caseInsensitiveContains(FolderFlags::Drafts, flags);
473 bool trash = caseInsensitiveContains(FolderFlags::Trash, flags);
474 bool isgmailParent = mailbox.name.toLower() == "[gmail]";
475 /**
476 * Because gmail duplicates messages all over the place we only support a few selected folders for now that should be mostly exclusive.
477 */
478 if (!(inbox || sent || drafts || trash || isgmailParent)) {
479 return;
480 }
481 }
453 SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed; 482 SinkLog() << "Found mailbox: " << mailbox.name << flags << FolderFlags::Noselect << noselect << " sub: " << subscribed;
454 auto ns = getNamespace(mailbox.name); 483 auto ns = getNamespace(mailbox.name);
455 callback(Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags}); 484 callback(Folder{mailbox.name, ns, mailbox.separator, noselect, subscribed, flags});
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h
index 58c49a2..0d70ba5 100644
--- a/examples/imapresource/imapserverproxy.h
+++ b/examples/imapresource/imapserverproxy.h
@@ -61,6 +61,7 @@ namespace FolderFlags
61 extern const char* Junk; 61 extern const char* Junk;
62 extern const char* Flagged; 62 extern const char* Flagged;
63 extern const char* All; 63 extern const char* All;
64 extern const char* Drafts;
64} 65}
65 66
66namespace Capabilities 67namespace Capabilities
@@ -79,6 +80,8 @@ struct Message {
79 bool fullPayload; 80 bool fullPayload;
80}; 81};
81 82
83bool flagsContain(const QByteArray &f, const QByteArrayList &flags);
84
82struct Folder { 85struct Folder {
83 Folder() = default; 86 Folder() = default;
84 Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_) 87 Folder(const QString &path, const QString &ns, const QChar &separator, bool noselect_, bool subscribed_, const QByteArrayList &flags_)
@@ -226,11 +229,6 @@ private:
226}; 229};
227 230
228class ImapServerProxy { 231class ImapServerProxy {
229 KIMAP2::Session *mSession;
230 QStringList mCapabilities;
231 Namespaces mNamespaces;
232
233
234public: 232public:
235 ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr); 233 ImapServerProxy(const QString &serverUrl, int port, SessionCache *sessionCache = nullptr);
236 234
@@ -280,9 +278,14 @@ public:
280 KAsync::Job<QVector<qint64>> fetchUids(const Folder &folder); 278 KAsync::Job<QVector<qint64>> fetchUids(const Folder &folder);
281 279
282private: 280private:
281 bool isGmail() const;
282
283 QString getNamespace(const QString &name); 283 QString getNamespace(const QString &name);
284 QObject mGuard; 284 QObject mGuard;
285 SessionCache *mSessionCache; 285 SessionCache *mSessionCache;
286 KIMAP2::Session *mSession;
287 QStringList mCapabilities;
288 Namespaces mNamespaces;
286}; 289};
287 290
288} 291}