diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-04-21 12:06:49 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-04-21 12:07:39 +0200 |
commit | 8d20128618b427270c6c7db49a1716f65b8ff840 (patch) | |
tree | 1329134c8166040d6719aaa6a5f542dc67f578fc /examples | |
parent | ac73ca1d2a23d3b62cca20545e019355f9d00035 (diff) | |
download | sink-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.
Diffstat (limited to 'examples')
-rw-r--r-- | examples/imapresource/imapresource.cpp | 36 | ||||
-rw-r--r-- | examples/imapresource/imapserverproxy.cpp | 31 | ||||
-rw-r--r-- | examples/imapresource/imapserverproxy.h | 13 |
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 | ||
90 | static 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 | |||
104 | static bool hasSpecialPurposeFlag(const QByteArrayList &flags) | ||
105 | { | ||
106 | return !getSpecialPurposeType(flags).isEmpty(); | ||
107 | } | ||
108 | |||
90 | 109 | ||
91 | class ImapSynchronizer : public Sink::Synchronizer { | 110 | class 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"; | |||
57 | const char* Imap::FolderFlags::Archive = "\\Archive"; | 57 | const char* Imap::FolderFlags::Archive = "\\Archive"; |
58 | const char* Imap::FolderFlags::Junk = "\\Junk"; | 58 | const char* Imap::FolderFlags::Junk = "\\Junk"; |
59 | const char* Imap::FolderFlags::Flagged = "\\Flagged"; | 59 | const char* Imap::FolderFlags::Flagged = "\\Flagged"; |
60 | const char* Imap::FolderFlags::Drafts = "\\Drafts"; | ||
60 | 61 | ||
61 | const char* Imap::Capabilities::Namespace = "NAMESPACE"; | 62 | const char* Imap::Capabilities::Namespace = "NAMESPACE"; |
62 | const char* Imap::Capabilities::Uidplus = "UIDPLUS"; | 63 | const char* Imap::Capabilities::Uidplus = "UIDPLUS"; |
@@ -192,6 +193,12 @@ KAsync::Job<void> ImapServerProxy::logout() | |||
192 | } | 193 | } |
193 | } | 194 | } |
194 | 195 | ||
196 | bool ImapServerProxy::isGmail() const | ||
197 | { | ||
198 | //Magic capability that only gmail has | ||
199 | return mCapabilities.contains("X-GM-EXT-1"); | ||
200 | } | ||
201 | |||
195 | KAsync::Job<SelectResult> ImapServerProxy::select(const QString &mailbox) | 202 | KAsync::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 | ||
451 | static bool caseInsensitiveContains(const QByteArray &f, const QByteArrayList &list) { | ||
452 | return list.contains(f) || list.contains(f.toLower()); | ||
453 | } | ||
454 | |||
455 | bool Imap::flagsContain(const QByteArray &f, const QByteArrayList &flags) | ||
456 | { | ||
457 | return caseInsensitiveContains(f, flags); | ||
458 | } | ||
459 | |||
444 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const Folder &)> callback) | 460 | KAsync::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 | ||
66 | namespace Capabilities | 67 | namespace Capabilities |
@@ -79,6 +80,8 @@ struct Message { | |||
79 | bool fullPayload; | 80 | bool fullPayload; |
80 | }; | 81 | }; |
81 | 82 | ||
83 | bool flagsContain(const QByteArray &f, const QByteArrayList &flags); | ||
84 | |||
82 | struct Folder { | 85 | struct 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 | ||
228 | class ImapServerProxy { | 231 | class ImapServerProxy { |
229 | KIMAP2::Session *mSession; | ||
230 | QStringList mCapabilities; | ||
231 | Namespaces mNamespaces; | ||
232 | |||
233 | |||
234 | public: | 232 | public: |
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 | ||
282 | private: | 280 | private: |
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 | } |