summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/imapresource/imapresource.cpp108
-rw-r--r--examples/imapresource/imapresource.h3
-rw-r--r--examples/imapresource/imapserverproxy.cpp76
-rw-r--r--examples/imapresource/imapserverproxy.h22
-rw-r--r--examples/imapresource/tests/imapresourcetest.cpp30
-rw-r--r--examples/imapresource/tests/imapserverproxytest.cpp50
6 files changed, 232 insertions, 57 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
index 49cbb20..6adfeb3 100644
--- a/examples/imapresource/imapresource.cpp
+++ b/examples/imapresource/imapresource.cpp
@@ -59,6 +59,8 @@ ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPo
59 auto config = ResourceConfig::getConfiguration(instanceIdentifier); 59 auto config = ResourceConfig::getConfiguration(instanceIdentifier);
60 mServer = config.value("server").toString(); 60 mServer = config.value("server").toString();
61 mPort = config.value("port").toInt(); 61 mPort = config.value("port").toInt();
62 mUser = config.value("user").toString();
63 mPassword = config.value("password").toString();
62 64
63 // auto folderUpdater = new FolderUpdater(QByteArray()); 65 // auto folderUpdater = new FolderUpdater(QByteArray());
64 addType(ENTITY_TYPE_MAIL, mMailAdaptorFactory, 66 addType(ENTITY_TYPE_MAIL, mMailAdaptorFactory,
@@ -117,6 +119,15 @@ void ImapResource::synchronizeFolders(const QVector<Folder> &folderList, Sink::S
117 } 119 }
118} 120}
119 121
122static QByteArray remoteIdForMessage(const QString &path, qint64 uid)
123{
124 return path.toUtf8() + "/" + QByteArray::number(uid);
125}
126
127static qint64 uidFromMessageRemoteId(const QByteArray &remoteId)
128{
129 return remoteId.split('/').last().toLongLong();
130}
120void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QVector<Message> &messages) 131void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QVector<Message> &messages)
121{ 132{
122 auto time = QSharedPointer<QTime>::create(); 133 auto time = QSharedPointer<QTime>::create();
@@ -130,23 +141,6 @@ void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sin
130 141
131 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction); 142 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction);
132 143
133 //This is not a full listing
134 // auto property = "folder";
135 // scanForRemovals(transaction, synchronizationTransaction, bufferType,
136 // [&](const std::function<void(const QByteArray &)> &callback) {
137 // Index index(bufferType + ".index." + property, transaction);
138 // index.lookup(folderLocalId, [&](const QByteArray &sinkId) {
139 // callback(sinkId);
140 // },
141 // [&](const Index::Error &error) {
142 // Warning() << "Error in index: " << error.message << property;
143 // });
144 // },
145 // [](const QByteArray &remoteId) -> bool {
146 // return QFile(remoteId).exists();
147 // }
148 // );
149
150 mSynchronizerQueue.startTransaction(); 144 mSynchronizerQueue.startTransaction();
151 int count = 0; 145 int count = 0;
152 for (const auto &message : messages) { 146 for (const auto &message : messages) {
@@ -170,8 +164,8 @@ void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sin
170 file.write(content); 164 file.write(content);
171 mail.setMimeMessagePath(filePath); 165 mail.setMimeMessagePath(filePath);
172 //FIXME Not sure if these are the actual flags 166 //FIXME Not sure if these are the actual flags
173 mail.setUnread(message.flags.contains("\\SEEN")); 167 mail.setUnread(!message.flags.contains(Imap::Flags::Seen));
174 mail.setImportant(message.flags.contains("\\FLAGGED")); 168 mail.setImportant(message.flags.contains(Imap::Flags::Flagged));
175 169
176 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail); 170 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail);
177 } 171 }
@@ -180,11 +174,56 @@ void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sin
180 Log() << "Synchronized " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]"; 174 Log() << "Synchronized " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
181} 175}
182 176
177void ImapResource::synchronizeRemovals(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QSet<qint64> &messages)
178{
179 auto time = QSharedPointer<QTime>::create();
180 time->start();
181 const QByteArray bufferType = ENTITY_TYPE_MAIL;
182
183 Trace() << "Finding removed mail.";
184
185 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction);
186
187 int count = 0;
188 auto property = Sink::ApplicationDomain::Mail::Folder::name;
189 scanForRemovals(transaction, synchronizationTransaction, bufferType,
190 [&](const std::function<void(const QByteArray &)> &callback) {
191 Index index(bufferType + ".index." + property, transaction);
192 index.lookup(folderLocalId, [&](const QByteArray &sinkId) {
193 callback(sinkId);
194 },
195 [&](const Index::Error &error) {
196 Warning() << "Error in index: " << error.message << property;
197 });
198 },
199 [messages, path, &count](const QByteArray &remoteId) -> bool {
200 if (messages.contains(uidFromMessageRemoteId(remoteId))) {
201 return true;
202 }
203 count++;
204 return false;
205 }
206 );
207
208 const auto elapsed = time->elapsed();
209 Log() << "Removed " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
210}
211
183KAsync::Job<void> ImapResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore) 212KAsync::Job<void> ImapResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore)
184{ 213{
185 Log() << " Synchronizing"; 214 Log() << " Synchronizing";
186 return KAsync::start<void>([this, &mainStore, &synchronizationStore](KAsync::Future<void> future) { 215 return KAsync::start<void>([this, &mainStore, &synchronizationStore](KAsync::Future<void> future) {
187 ImapServerProxy imap(mServer, mPort); 216 ImapServerProxy imap(mServer, mPort);
217 auto loginFuture = imap.login(mUser, mPassword).exec();
218 loginFuture.waitForFinished();
219 if (loginFuture.errorCode()) {
220 Warning() << "Login failed.";
221 future.setError(1, "Login failed");
222 return;
223 } else {
224 Trace() << "Login was successful";
225 }
226
188 QVector<Folder> folderList; 227 QVector<Folder> folderList;
189 auto folderFuture = imap.fetchFolders([this, &imap, &mainStore, &synchronizationStore, &folderList](const QVector<Folder> &folders) { 228 auto folderFuture = imap.fetchFolders([this, &imap, &mainStore, &synchronizationStore, &folderList](const QVector<Folder> &folders) {
190 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly); 229 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
@@ -197,8 +236,11 @@ KAsync::Job<void> ImapResource::synchronizeWithSource(Sink::Storage &mainStore,
197 }); 236 });
198 folderFuture.waitForFinished(); 237 folderFuture.waitForFinished();
199 if (folderFuture.errorCode()) { 238 if (folderFuture.errorCode()) {
239 Warning() << "Folder sync failed.";
200 future.setError(1, "Folder list sync failed"); 240 future.setError(1, "Folder list sync failed");
201 return; 241 return;
242 } else {
243 Trace() << "Folder sync was successful";
202 } 244 }
203 245
204 for (const auto &folder : folderList) { 246 for (const auto &folder : folderList) {
@@ -220,30 +262,32 @@ KAsync::Job<void> ImapResource::synchronizeWithSource(Sink::Storage &mainStore,
220 // transaction.commit(); 262 // transaction.commit();
221 // syncTransaction.commit(); 263 // syncTransaction.commit();
222 264
223 auto messagesFuture = imap.fetchMessages(folder, [this, &mainStore, &synchronizationStore, folder](const QVector<Message> &messages) { 265 QSet<qint64> uids;
266 auto messagesFuture = imap.fetchMessages(folder, [this, &mainStore, &synchronizationStore, folder, &uids](const QVector<Message> &messages) {
224 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly); 267 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
225 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite); 268 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
226 Trace() << "Synchronizing mails" << folder.pathParts.join('/'); 269 Trace() << "Synchronizing mails" << folder.normalizedPath();
227 synchronizeMails(transaction, syncTransaction, folder.pathParts.join('/'), messages); 270 for (const auto &msg : messages) {
271 uids << msg.uid;
272 }
273 synchronizeMails(transaction, syncTransaction, folder.normalizedPath(), messages);
228 transaction.commit(); 274 transaction.commit();
229 syncTransaction.commit(); 275 syncTransaction.commit();
230 }); 276 });
231 messagesFuture.waitForFinished(); 277 messagesFuture.waitForFinished();
232 if (messagesFuture.errorCode()) { 278 if (messagesFuture.errorCode()) {
233 future.setError(1, "Folder sync failed: " + folder.pathParts.join('/')); 279 future.setError(1, "Folder sync failed: " + folder.normalizedPath());
234 return; 280 return;
235 } 281 }
236 Trace() << "Folder synchronized: " << folder.pathParts.join('/'); 282 //Remove what there is to remove
283 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
284 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
285 synchronizeRemovals(transaction, syncTransaction, folder.normalizedPath(), uids);
286 transaction.commit();
287 syncTransaction.commit();
288 Trace() << "Folder synchronized: " << folder.normalizedPath();
237 } 289 }
238 290
239
240 // auto transaction = mainStore.createTransaction(Sink::Storage::ReadWrite);
241 // auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_FOLDER);
242 // mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &data) {
243 // return true;
244 // });
245 //TODO now fetch all folders and iterate over them and synchronize each one
246
247 Log() << "Done Synchronizing"; 291 Log() << "Done Synchronizing";
248 future.setFinished(); 292 future.setFinished();
249 }); 293 });
diff --git a/examples/imapresource/imapresource.h b/examples/imapresource/imapresource.h
index 23b7e1a..82f96a4 100644
--- a/examples/imapresource/imapresource.h
+++ b/examples/imapresource/imapresource.h
@@ -52,12 +52,15 @@ private:
52 QByteArray createFolder(const QString &folderPath, const QByteArray &icon, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction); 52 QByteArray createFolder(const QString &folderPath, const QByteArray &icon, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction);
53 void synchronizeFolders(const QVector<Imap::Folder> &folderList, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction); 53 void synchronizeFolders(const QVector<Imap::Folder> &folderList, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction);
54 void synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QVector<Imap::Message> &messages); 54 void synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QVector<Imap::Message> &messages);
55 void synchronizeRemovals(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QSet<qint64> &messages);
55 56
56 QSharedPointer<ImapMailAdaptorFactory> mMailAdaptorFactory; 57 QSharedPointer<ImapMailAdaptorFactory> mMailAdaptorFactory;
57 QSharedPointer<ImapFolderAdaptorFactory> mFolderAdaptorFactory; 58 QSharedPointer<ImapFolderAdaptorFactory> mFolderAdaptorFactory;
58private: 59private:
59 QString mServer; 60 QString mServer;
60 int mPort; 61 int mPort;
62 QString mUser;
63 QString mPassword;
61}; 64};
62 65
63class ImapResourceFactory : public Sink::ResourceFactory 66class ImapResourceFactory : public Sink::ResourceFactory
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp
index 836a9bc..dfb134c 100644
--- a/examples/imapresource/imapserverproxy.cpp
+++ b/examples/imapresource/imapserverproxy.cpp
@@ -24,6 +24,9 @@
24#include <KIMAP/KIMAP/SelectJob> 24#include <KIMAP/KIMAP/SelectJob>
25#include <KIMAP/KIMAP/AppendJob> 25#include <KIMAP/KIMAP/AppendJob>
26#include <KIMAP/KIMAP/CreateJob> 26#include <KIMAP/KIMAP/CreateJob>
27#include <KIMAP/KIMAP/DeleteJob>
28#include <KIMAP/KIMAP/StoreJob>
29#include <KIMAP/KIMAP/ExpungeJob>
27 30
28#include <KIMAP/KIMAP/SessionUiProxy> 31#include <KIMAP/KIMAP/SessionUiProxy>
29#include <KCoreAddons/KJob> 32#include <KCoreAddons/KJob>
@@ -32,10 +35,15 @@
32 35
33using namespace Imap; 36using namespace Imap;
34 37
38const char* Imap::Flags::Seen = "\\Seen";
39const char* Imap::Flags::Deleted = "\\Deleted";
40const char* Imap::Flags::Answered = "\\Answered";
41const char* Imap::Flags::Flagged = "\\Flagged";
42
35static KAsync::Job<void> runJob(KJob *job) 43static KAsync::Job<void> runJob(KJob *job)
36{ 44{
37 return KAsync::start<void>([job](KAsync::Future<void> &future) { 45 return KAsync::start<void>([job](KAsync::Future<void> &future) {
38 QObject::connect(job, &KJob::result, job, [&future](KJob *job) { 46 QObject::connect(job, &KJob::result, [&future](KJob *job) {
39 if (job->error()) { 47 if (job->error()) {
40 Warning() << "Job failed: " << job->errorString(); 48 Warning() << "Job failed: " << job->errorString();
41 future.setError(job->error(), job->errorString()); 49 future.setError(job->error(), job->errorString());
@@ -62,9 +70,6 @@ ImapServerProxy::ImapServerProxy(const QString &serverUrl, int port) : mSession(
62 70
63KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString &password) 71KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString &password)
64{ 72{
65 if (mSession->state() == KIMAP::Session::State::Authenticated || mSession->state() == KIMAP::Session::State::Selected) {
66 return KAsync::null<void>();
67 }
68 auto loginJob = new KIMAP::LoginJob(mSession); 73 auto loginJob = new KIMAP::LoginJob(mSession);
69 loginJob->setUserName(username); 74 loginJob->setUserName(username);
70 loginJob->setPassword(password); 75 loginJob->setPassword(password);
@@ -75,9 +80,6 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString
75 80
76KAsync::Job<void> ImapServerProxy::select(const QString &mailbox) 81KAsync::Job<void> ImapServerProxy::select(const QString &mailbox)
77{ 82{
78 if (mSession->state() == KIMAP::Session::State::Disconnected) {
79 return KAsync::error<void>(1, "Not connected");
80 }
81 auto select = new KIMAP::SelectJob(mSession); 83 auto select = new KIMAP::SelectJob(mSession);
82 select->setMailBox(mailbox); 84 select->setMailBox(mailbox);
83 // select->setCondstoreEnabled(serverSupportsCondstore()); 85 // select->setCondstoreEnabled(serverSupportsCondstore());
@@ -86,9 +88,6 @@ KAsync::Job<void> ImapServerProxy::select(const QString &mailbox)
86 88
87KAsync::Job<void> ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags, const QDateTime &internalDate) 89KAsync::Job<void> ImapServerProxy::append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags, const QDateTime &internalDate)
88{ 90{
89 if (mSession->state() == KIMAP::Session::State::Disconnected) {
90 return KAsync::error<void>(1, "Not connected");
91 }
92 auto append = new KIMAP::AppendJob(mSession); 91 auto append = new KIMAP::AppendJob(mSession);
93 append->setMailBox(mailbox); 92 append->setMailBox(mailbox);
94 append->setContent(content); 93 append->setContent(content);
@@ -97,21 +96,38 @@ KAsync::Job<void> ImapServerProxy::append(const QString &mailbox, const QByteArr
97 return runJob(append); 96 return runJob(append);
98} 97}
99 98
99KAsync::Job<void> ImapServerProxy::store(const KIMAP::ImapSet &set, const QList<QByteArray> &flags)
100{
101 auto store = new KIMAP::StoreJob(mSession);
102 store->setUidBased(true);
103 store->setSequenceSet(set);
104 store->setFlags(flags);
105 store->setMode(KIMAP::StoreJob::AppendFlags);
106 return runJob(store);
107}
108
100KAsync::Job<void> ImapServerProxy::create(const QString &mailbox) 109KAsync::Job<void> ImapServerProxy::create(const QString &mailbox)
101{ 110{
102 if (mSession->state() == KIMAP::Session::State::Disconnected) {
103 return KAsync::error<void>(1, "Not connected");
104 }
105 auto create = new KIMAP::CreateJob(mSession); 111 auto create = new KIMAP::CreateJob(mSession);
106 create->setMailBox(mailbox); 112 create->setMailBox(mailbox);
107 return runJob(create); 113 return runJob(create);
108} 114}
109 115
116KAsync::Job<void> ImapServerProxy::remove(const QString &mailbox)
117{
118 auto job = new KIMAP::DeleteJob(mSession);
119 job->setMailBox(mailbox);
120 return runJob(job);
121}
122
123KAsync::Job<void> ImapServerProxy::expunge()
124{
125 auto job = new KIMAP::ExpungeJob(mSession);
126 return runJob(job);
127}
128
110KAsync::Job<void> ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::FetchJob::FetchScope scope, FetchCallback callback) 129KAsync::Job<void> ImapServerProxy::fetch(const KIMAP::ImapSet &set, KIMAP::FetchJob::FetchScope scope, FetchCallback callback)
111{ 130{
112 if (mSession->state() == KIMAP::Session::State::Disconnected) {
113 return KAsync::error<void>(1, "Not connected");
114 }
115 auto fetch = new KIMAP::FetchJob(mSession); 131 auto fetch = new KIMAP::FetchJob(mSession);
116 fetch->setSequenceSet(set); 132 fetch->setSequenceSet(set);
117 fetch->setUidBased(true); 133 fetch->setUidBased(true);
@@ -164,34 +180,48 @@ KAsync::Job<void> ImapServerProxy::list(KIMAP::ListJob::Option option, const std
164 // listJob->setQueriedNamespaces(serverNamespaces()); 180 // listJob->setQueriedNamespaces(serverNamespaces());
165 QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived, 181 QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived,
166 listJob, callback); 182 listJob, callback);
183 //Figure out the separator character on the first list issued.
184 if (mSeparatorCharacter.isNull()) {
185 QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived,
186 listJob, [this](const QList<KIMAP::MailBoxDescriptor> &mailboxes,const QList<QList<QByteArray> > &flags) {
187 if (!mailboxes.isEmpty() && mSeparatorCharacter.isNull()) {
188 mSeparatorCharacter = mailboxes.first().separator;
189 }
190 }
191 );
192 }
167 return runJob(listJob); 193 return runJob(listJob);
168} 194}
169 195
196KAsync::Job<void> ImapServerProxy::remove(const QString &mailbox, const QByteArray &imapSet)
197{
198 const auto set = KIMAP::ImapSet::fromImapSequenceSet(imapSet);
199 return select(mailbox).then<void>(store(set, QByteArrayList() << Flags::Deleted)).then<void>(expunge());
200}
201
170KAsync::Future<void> ImapServerProxy::fetchFolders(std::function<void(const QVector<Folder> &)> callback) 202KAsync::Future<void> ImapServerProxy::fetchFolders(std::function<void(const QVector<Folder> &)> callback)
171{ 203{
172 Trace() << "Fetching folders"; 204 Trace() << "Fetching folders";
173 auto job = login("doe", "doe").then<void>(list(KIMAP::ListJob::IncludeUnsubscribed, [callback](const QList<KIMAP::MailBoxDescriptor> &mailboxes, const QList<QList<QByteArray> > &flags){ 205 auto job = list(KIMAP::ListJob::IncludeUnsubscribed, [callback](const QList<KIMAP::MailBoxDescriptor> &mailboxes, const QList<QList<QByteArray> > &flags){
174 QVector<Folder> list; 206 QVector<Folder> list;
175 for (const auto &mailbox : mailboxes) { 207 for (const auto &mailbox : mailboxes) {
176 Trace() << "Found mailbox: " << mailbox.name; 208 Trace() << "Found mailbox: " << mailbox.name;
177 list << Folder{mailbox.name.split(mailbox.separator)}; 209 list << Folder{mailbox.name.split(mailbox.separator)};
178 } 210 }
179 callback(list); 211 callback(list);
180 }),
181 [](int errorCode, const QString &errorString) {
182 Warning() << "Failed to list folders: " << errorCode << errorString;
183 }); 212 });
184 return job.exec(); 213 return job.exec();
185} 214}
186 215
187KAsync::Future<void> ImapServerProxy::fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback) 216KAsync::Future<void> ImapServerProxy::fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback)
188{ 217{
189 //TODO use the right separator 218 Q_ASSERT(!mSeparatorCharacter.isNull());
190 auto job = login("doe", "doe").then<void>(select(folder.pathParts.join('.'))).then<void, KAsync::Job<void>>([this, callback, folder]() -> KAsync::Job<void> { 219 auto job = select(folder.pathParts.join(mSeparatorCharacter)).then<void, KAsync::Job<void>>([this, callback, folder]() -> KAsync::Job<void> {
191 return fetchHeaders(folder.pathParts.join('.')).then<void, KAsync::Job<void>, QList<qint64>>([this, callback](const QList<qint64> &uidsToFetch){ 220 return fetchHeaders(folder.pathParts.join(mSeparatorCharacter)).then<void, KAsync::Job<void>, QList<qint64>>([this, callback](const QList<qint64> &uidsToFetch){
192 Trace() << "Uids to fetch: " << uidsToFetch; 221 Trace() << "Uids to fetch: " << uidsToFetch;
193 if (uidsToFetch.isEmpty()) { 222 if (uidsToFetch.isEmpty()) {
194 Trace() << "Nothing to fetch"; 223 Trace() << "Nothing to fetch";
224 callback(QVector<Message>());
195 return KAsync::null<void>(); 225 return KAsync::null<void>();
196 } 226 }
197 KIMAP::FetchJob::FetchScope scope; 227 KIMAP::FetchJob::FetchScope scope;
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h
index 475a45d..7000c67 100644
--- a/examples/imapresource/imapserverproxy.h
+++ b/examples/imapresource/imapserverproxy.h
@@ -28,6 +28,18 @@
28 28
29namespace Imap { 29namespace Imap {
30 30
31namespace Flags
32{
33 /// The flag for a message being seen (i.e. opened by user).
34 extern const char* Seen;
35 /// The flag for a message being deleted by the user.
36 extern const char* Deleted;
37 /// The flag for a message being replied to by the user.
38 extern const char* Answered;
39 /// The flag for a message being marked as flagged.
40 extern const char* Flagged;
41}
42
31struct Message { 43struct Message {
32 qint64 uid; 44 qint64 uid;
33 qint64 size; 45 qint64 size;
@@ -37,11 +49,17 @@ struct Message {
37}; 49};
38 50
39struct Folder { 51struct Folder {
52 QString normalizedPath() const
53 {
54 return pathParts.join('/');
55 }
56
40 QList<QString> pathParts; 57 QList<QString> pathParts;
41}; 58};
42 59
43class ImapServerProxy { 60class ImapServerProxy {
44 KIMAP::Session *mSession; 61 KIMAP::Session *mSession;
62 QChar mSeparatorCharacter;
45public: 63public:
46 ImapServerProxy(const QString &serverUrl, int port); 64 ImapServerProxy(const QString &serverUrl, int port);
47 65
@@ -49,7 +67,10 @@ public:
49 KAsync::Job<void> login(const QString &username, const QString &password); 67 KAsync::Job<void> login(const QString &username, const QString &password);
50 KAsync::Job<void> select(const QString &mailbox); 68 KAsync::Job<void> select(const QString &mailbox);
51 KAsync::Job<void> append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags = QList<QByteArray>(), const QDateTime &internalDate = QDateTime()); 69 KAsync::Job<void> append(const QString &mailbox, const QByteArray &content, const QList<QByteArray> &flags = QList<QByteArray>(), const QDateTime &internalDate = QDateTime());
70 KAsync::Job<void> store(const KIMAP::ImapSet &set, const QList<QByteArray> &flags);
52 KAsync::Job<void> create(const QString &mailbox); 71 KAsync::Job<void> create(const QString &mailbox);
72 KAsync::Job<void> remove(const QString &mailbox);
73 KAsync::Job<void> expunge();
53 74
54 typedef std::function<void(const QString &, 75 typedef std::function<void(const QString &,
55 const QMap<qint64,qint64> &, 76 const QMap<qint64,qint64> &,
@@ -63,6 +84,7 @@ public:
63 84
64 //Composed calls that do login etc. 85 //Composed calls that do login etc.
65 KAsync::Job<QList<qint64>> fetchHeaders(const QString &mailbox); 86 KAsync::Job<QList<qint64>> fetchHeaders(const QString &mailbox);
87 KAsync::Job<void> remove(const QString &mailbox, const QByteArray &imapSet);
66 88
67 KAsync::Future<void> fetchFolders(std::function<void(const QVector<Folder> &)> callback); 89 KAsync::Future<void> fetchFolders(std::function<void(const QVector<Folder> &)> callback);
68 KAsync::Future<void> fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback); 90 KAsync::Future<void> fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback);
diff --git a/examples/imapresource/tests/imapresourcetest.cpp b/examples/imapresource/tests/imapresourcetest.cpp
index 8ce32c2..30bb953 100644
--- a/examples/imapresource/tests/imapresourcetest.cpp
+++ b/examples/imapresource/tests/imapresourcetest.cpp
@@ -65,6 +65,8 @@ private slots:
65 resource.setProperty("identifier", "org.kde.imap.instance1"); 65 resource.setProperty("identifier", "org.kde.imap.instance1");
66 resource.setProperty("type", "org.kde.imap"); 66 resource.setProperty("type", "org.kde.imap");
67 resource.setProperty("server", "localhost"); 67 resource.setProperty("server", "localhost");
68 resource.setProperty("user", "doe");
69 resource.setProperty("password", "doe");
68 resource.setProperty("port", 993); 70 resource.setProperty("port", 993);
69 Sink::Store::create(resource).exec().waitForFinished(); 71 Sink::Store::create(resource).exec().waitForFinished();
70 } 72 }
@@ -164,8 +166,8 @@ private slots:
164 VERIFYEXEC(Store::synchronize(query)); 166 VERIFYEXEC(Store::synchronize(query));
165 ResourceControl::flushMessageQueue(query.resources).exec().waitForFinished(); 167 ResourceControl::flushMessageQueue(query.resources).exec().waitForFinished();
166 168
167 ImapServerProxy imap("localhost", 993); 169 Imap::ImapServerProxy imap("localhost", 993);
168 imap.login("doe", "doe").exec().waitForFinished(); 170 VERIFYEXEC(imap.login("doe", "doe"));
169 171
170 auto msg = KMime::Message::Ptr::create(); 172 auto msg = KMime::Message::Ptr::create();
171 msg->subject(true)->fromUnicodeString("Foobar", "utf8"); 173 msg->subject(true)->fromUnicodeString("Foobar", "utf8");
@@ -182,6 +184,30 @@ private slots:
182 VERIFYEXEC(job); 184 VERIFYEXEC(job);
183 } 185 }
184 186
187 void testFetchRemovedMessages()
188 {
189 Sink::Query query;
190 query.resources << "org.kde.imap.instance1";
191 query.request<Mail::Subject>().request<Mail::MimeMessage>();
192
193 // Ensure all local data is processed
194 VERIFYEXEC(Store::synchronize(query));
195 ResourceControl::flushMessageQueue(query.resources).exec().waitForFinished();
196
197 Imap::ImapServerProxy imap("localhost", 993);
198 VERIFYEXEC(imap.login("doe", "doe"));
199
200 VERIFYEXEC(imap.remove("INBOX.test", "2:*"));
201
202 Store::synchronize(query).exec().waitForFinished();
203 ResourceControl::flushMessageQueue(query.resources).exec().waitForFinished();
204
205 auto job = Store::fetchAll<Mail>(query).then<void, QList<Mail::Ptr>>([](const QList<Mail::Ptr> &mails) {
206 QCOMPARE(mails.size(), 1);
207 });
208 VERIFYEXEC(job);
209 }
210
185 void testFailingSync() 211 void testFailingSync()
186 { 212 {
187 auto resource = ApplicationDomain::ImapResource::create("account1"); 213 auto resource = ApplicationDomain::ImapResource::create("account1");
diff --git a/examples/imapresource/tests/imapserverproxytest.cpp b/examples/imapresource/tests/imapserverproxytest.cpp
index 139597a..b30cc33 100644
--- a/examples/imapresource/tests/imapserverproxytest.cpp
+++ b/examples/imapresource/tests/imapserverproxytest.cpp
@@ -79,6 +79,7 @@ private slots:
79 void testFetchFolders() 79 void testFetchFolders()
80 { 80 {
81 ImapServerProxy imap("localhost", 993); 81 ImapServerProxy imap("localhost", 993);
82 VERIFYEXEC(imap.login("doe", "doe"));
82 auto future = imap.fetchFolders([](const QVector<Folder> &){}); 83 auto future = imap.fetchFolders([](const QVector<Folder> &){});
83 future.waitForFinished(); 84 future.waitForFinished();
84 QVERIFY(!future.errorCode()); 85 QVERIFY(!future.errorCode());
@@ -93,6 +94,55 @@ private slots:
93 QVERIFY(future2.errorCode()); 94 QVERIFY(future2.errorCode());
94 } 95 }
95 96
97 void testFetchMail()
98 {
99 ImapServerProxy imap("localhost", 993);
100 VERIFYEXEC(imap.login("doe", "doe"));
101
102 KIMAP::FetchJob::FetchScope scope;
103 scope.mode = KIMAP::FetchJob::FetchScope::Headers;
104 int count = 0;
105 auto job = imap.select("INBOX.test").then<void>(imap.fetch(KIMAP::ImapSet::fromImapSequenceSet("1:*"), scope,
106 [&count](const QString &mailbox,
107 const QMap<qint64,qint64> &uids,
108 const QMap<qint64,qint64> &sizes,
109 const QMap<qint64,KIMAP::MessageAttribute> &attrs,
110 const QMap<qint64,KIMAP::MessageFlags> &flags,
111 const QMap<qint64,KIMAP::MessagePtr> &messages) {
112 Trace() << "Received " << uids.size() << " messages from " << mailbox;
113 Trace() << uids.size() << sizes.size() << attrs.size() << flags.size() << messages.size();
114 count += uids.size();
115 }));
116
117 VERIFYEXEC(job);
118 QCOMPARE(count, 1);
119 }
120
121 void testRemoveMail()
122 {
123 ImapServerProxy imap("localhost", 993);
124 VERIFYEXEC(imap.login("doe", "doe"));
125 VERIFYEXEC(imap.remove("INBOX.test", "1:*"));
126
127 KIMAP::FetchJob::FetchScope scope;
128 scope.mode = KIMAP::FetchJob::FetchScope::Headers;
129 int count = 0;
130 auto job = imap.select("INBOX.test").then<void>(imap.fetch(KIMAP::ImapSet::fromImapSequenceSet("1:*"), scope,
131 [&count](const QString &mailbox,
132 const QMap<qint64,qint64> &uids,
133 const QMap<qint64,qint64> &sizes,
134 const QMap<qint64,KIMAP::MessageAttribute> &attrs,
135 const QMap<qint64,KIMAP::MessageFlags> &flags,
136 const QMap<qint64,KIMAP::MessagePtr> &messages) {
137 Trace() << "Received " << uids.size() << " messages from " << mailbox;
138 Trace() << uids.size() << sizes.size() << attrs.size() << flags.size() << messages.size();
139 count += uids.size();
140 }));
141
142 VERIFYEXEC(job);
143 QCOMPARE(count, 0);
144 }
145
96}; 146};
97 147
98QTEST_MAIN(ImapServerProxyTest) 148QTEST_MAIN(ImapServerProxyTest)