summaryrefslogtreecommitdiffstats
path: root/examples/imapresource/imapresource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/imapresource/imapresource.cpp')
-rw-r--r--examples/imapresource/imapresource.cpp558
1 files changed, 341 insertions, 217 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
index 61031d8..72cc058 100644
--- a/examples/imapresource/imapresource.cpp
+++ b/examples/imapresource/imapresource.cpp
@@ -35,6 +35,10 @@
35#include "facadefactory.h" 35#include "facadefactory.h"
36#include "indexupdater.h" 36#include "indexupdater.h"
37#include "inspection.h" 37#include "inspection.h"
38#include "synchronizer.h"
39#include "sourcewriteback.h"
40#include "entitystore.h"
41#include "remoteidmap.h"
38#include <QDate> 42#include <QDate>
39#include <QUuid> 43#include <QUuid>
40#include <QDir> 44#include <QDir>
@@ -51,14 +55,26 @@
51 55
52using namespace Imap; 56using namespace Imap;
53 57
54class FolderUpdater : public Sink::Preprocessor 58class MailPropertyExtractor : public Sink::Preprocessor
55{ 59{
56public: 60public:
57 FolderUpdater(const QByteArray &drafts) {} 61 MailPropertyExtractor() {}
58 62
59 void updatedIndexedProperties(Sink::ApplicationDomain::BufferAdaptor &newEntity) 63 void updatedIndexedProperties(Sink::ApplicationDomain::BufferAdaptor &newEntity)
60 { 64 {
61 const auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString(); 65 const auto mimeMessagePath = newEntity.getProperty("mimeMessage").toString();
66 // auto parts = mimeMessagePath.split('/');
67 // const auto key = parts.takeLast();
68 // const auto path = parts.join("/") + "/cur/";
69 //
70 // QDir dir(path);
71 // const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files);
72 // if (list.size() != 1) {
73 // Warning() << "Failed to find message " << path << key << list.size();
74 // return;
75 // }
76
77 // QString file;
62 QFile f(mimeMessagePath); 78 QFile f(mimeMessagePath);
63 if (!f.open(QIODevice::ReadOnly)) { 79 if (!f.open(QIODevice::ReadOnly)) {
64 Warning() << "Failed to open the file: " << mimeMessagePath; 80 Warning() << "Failed to open the file: " << mimeMessagePath;
@@ -75,6 +91,7 @@ public:
75 msg->parse(); 91 msg->parse();
76 92
77 newEntity.setProperty("subject", msg->subject(true)->asUnicodeString()); 93 newEntity.setProperty("subject", msg->subject(true)->asUnicodeString());
94 newEntity.setProperty("subject", msg->subject(true)->asUnicodeString());
78 newEntity.setProperty("sender", msg->from(true)->asUnicodeString()); 95 newEntity.setProperty("sender", msg->from(true)->asUnicodeString());
79 newEntity.setProperty("senderName", msg->from(true)->asUnicodeString()); 96 newEntity.setProperty("senderName", msg->from(true)->asUnicodeString());
80 newEntity.setProperty("date", msg->date(true)->dateTime()); 97 newEntity.setProperty("date", msg->date(true)->dateTime());
@@ -95,257 +112,268 @@ public:
95 { 112 {
96 } 113 }
97 114
98 QSharedPointer<ImapFolderAdaptorFactory> mFolderAdaptorFactory;
99}; 115};
100 116
101ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline) 117class ImapSynchronizer : public Sink::Synchronizer {
102 : Sink::GenericResource(instanceIdentifier, pipeline), 118public:
103 mMailAdaptorFactory(QSharedPointer<ImapMailAdaptorFactory>::create()), 119 ImapSynchronizer(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier)
104 mFolderAdaptorFactory(QSharedPointer<ImapFolderAdaptorFactory>::create()) 120 : Sink::Synchronizer(resourceType, resourceInstanceIdentifier)
105{ 121 {
106 auto config = ResourceConfig::getConfiguration(instanceIdentifier);
107 mServer = config.value("server").toString();
108 mPort = config.value("port").toInt();
109 mUser = config.value("user").toString();
110 mPassword = config.value("password").toString();
111
112 auto folderUpdater = new FolderUpdater(QByteArray());
113 folderUpdater->mFolderAdaptorFactory = mFolderAdaptorFactory;
114 122
115 addType(ENTITY_TYPE_MAIL, mMailAdaptorFactory, 123 }
116 QVector<Sink::Preprocessor*>() << folderUpdater << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>);
117 addType(ENTITY_TYPE_FOLDER, mFolderAdaptorFactory,
118 QVector<Sink::Preprocessor*>() << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>);
119}
120 124
121QByteArray ImapResource::createFolder(const QString &folderPath, const QByteArray &icon, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction) 125 QByteArray createFolder(const QString &folderPath, const QByteArray &icon)
122{ 126 {
123 auto remoteId = folderPath.toUtf8(); 127 auto remoteId = folderPath.toUtf8();
124 auto bufferType = ENTITY_TYPE_FOLDER; 128 auto bufferType = ENTITY_TYPE_FOLDER;
125 Sink::ApplicationDomain::Folder folder; 129 Sink::ApplicationDomain::Folder folder;
126 auto folderPathParts = folderPath.split('/'); 130 auto folderPathParts = folderPath.split('/');
127 const auto name = folderPathParts.takeLast(); 131 const auto name = folderPathParts.takeLast();
128 folder.setProperty("name", name); 132 folder.setProperty("name", name);
129 folder.setProperty("icon", icon); 133 folder.setProperty("icon", icon);
130 134
131 if (!folderPathParts.isEmpty()) { 135 if (!folderPathParts.isEmpty()) {
132 folder.setProperty("parent", resolveRemoteId(ENTITY_TYPE_FOLDER, folderPathParts.join('/').toUtf8(), synchronizationTransaction)); 136 folder.setProperty("parent", syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderPathParts.join('/').toUtf8()));
137 }
138 createOrModify(bufferType, remoteId, folder);
139 return remoteId;
133 } 140 }
134 createOrModify(transaction, synchronizationTransaction, *mFolderAdaptorFactory, bufferType, remoteId, folder);
135 return remoteId;
136}
137 141
138void ImapResource::synchronizeFolders(const QVector<Folder> &folderList, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction) 142 void synchronizeFolders(const QVector<Folder> &folderList)
139{ 143 {
140 const QByteArray bufferType = ENTITY_TYPE_FOLDER; 144 const QByteArray bufferType = ENTITY_TYPE_FOLDER;
141 Trace() << "Found folders " << folderList.size(); 145 Trace() << "Found folders " << folderList.size();
142 146
143 scanForRemovals(transaction, synchronizationTransaction, bufferType, 147 scanForRemovals(bufferType,
144 [&bufferType, &transaction](const std::function<void(const QByteArray &)> &callback) { 148 [this, &bufferType](const std::function<void(const QByteArray &)> &callback) {
145 //TODO Instead of iterating over all entries in the database, which can also pick up the same item multiple times, 149 //TODO Instead of iterating over all entries in the database, which can also pick up the same item multiple times,
146 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index, 150 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index,
147 //but we currently fail to iterate over all entries in an index it seems. 151 //but we currently fail to iterate over all entries in an index it seems.
148 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true); 152 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true);
149 auto mainDatabase = Sink::Storage::mainDatabase(transaction, bufferType); 153 auto mainDatabase = Sink::Storage::mainDatabase(transaction(), bufferType);
150 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) { 154 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) {
151 callback(key); 155 callback(key);
152 return true;
153 });
154 },
155 [&folderList](const QByteArray &remoteId) -> bool {
156 //folderList.contains(remoteId)
157 for (const auto folderPath : folderList) {
158 if (folderPath.pathParts.join('/') == remoteId) {
159 return true; 156 return true;
157 });
158 },
159 [&folderList](const QByteArray &remoteId) -> bool {
160 //folderList.contains(remoteId)
161 for (const auto folderPath : folderList) {
162 if (folderPath.pathParts.join('/') == remoteId) {
163 return true;
164 }
160 } 165 }
166 return false;
161 } 167 }
162 return false; 168 );
169
170 for (const auto folderPath : folderList) {
171 createFolder(folderPath.pathParts.join('/'), "folder");
163 } 172 }
164 ); 173 }
165 174
166 for (const auto folderPath : folderList) { 175 static QByteArray remoteIdForMessage(const QString &path, qint64 uid)
167 createFolder(folderPath.pathParts.join('/'), "folder", transaction, synchronizationTransaction); 176 {
177 return path.toUtf8() + "/" + QByteArray::number(uid);
168 } 178 }
169}
170 179
171static QByteArray remoteIdForMessage(const QString &path, qint64 uid) 180 static qint64 uidFromMessageRemoteId(const QByteArray &remoteId)
172{ 181 {
173 return path.toUtf8() + "/" + QByteArray::number(uid); 182 return remoteId.split('/').last().toLongLong();
174} 183 }
175 184
176static qint64 uidFromMessageRemoteId(const QByteArray &remoteId) 185 void synchronizeMails(const QString &path, const QVector<Message> &messages)
177{ 186 {
178 return remoteId.split('/').last().toLongLong(); 187 auto time = QSharedPointer<QTime>::create();
179} 188 time->start();
180void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QVector<Message> &messages) 189 const QByteArray bufferType = ENTITY_TYPE_MAIL;
181{
182 auto time = QSharedPointer<QTime>::create();
183 time->start();
184 const QByteArray bufferType = ENTITY_TYPE_MAIL;
185 190
186 191
187 Trace() << "Importing new mail."; 192 Trace() << "Importing new mail.";
188 193
189 // Trace() << "Looking into " << listingPath; 194 // Trace() << "Looking into " << listingPath;
190 195
191 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction); 196 const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8());
192 197
193 mSynchronizerQueue.startTransaction(); 198 int count = 0;
194 int count = 0; 199 for (const auto &message : messages) {
195 for (const auto &message : messages) { 200 count++;
196 count++; 201 const auto remoteId = path.toUtf8() + "/" + QByteArray::number(message.uid);
197 const auto remoteId = path.toUtf8() + "/" + QByteArray::number(message.uid);
198 202
199 Trace() << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags; 203 Trace() << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags;
200 204
201 Sink::ApplicationDomain::Mail mail; 205 Sink::ApplicationDomain::Mail mail;
202 mail.setFolder(folderLocalId); 206 mail.setFolder(folderLocalId);
203 //FIXME this should come from the mime message, extracted in the pipeline 207 //FIXME this should come from the mime message, extracted in the pipeline
204 mail.setExtractedSubject(message.msg->subject(true)->asUnicodeString()); 208 mail.setExtractedSubject(message.msg->subject(true)->asUnicodeString());
205 209
206 auto filePath = Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + remoteId; 210 auto filePath = Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + remoteId;
207 QDir().mkpath(Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + path.toUtf8()); 211 QDir().mkpath(Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + path.toUtf8());
208 QFile file(filePath); 212 QFile file(filePath);
209 if (!file.open(QIODevice::WriteOnly)) { 213 if (!file.open(QIODevice::WriteOnly)) {
210 Warning() << "Failed to open file for writing: " << file.errorString(); 214 Warning() << "Failed to open file for writing: " << file.errorString();
215 }
216 const auto content = message.msg->encodedContent();
217 file.write(content);
218 mail.setMimeMessagePath(filePath);
219 //FIXME Not sure if these are the actual flags
220 mail.setUnread(!message.flags.contains(Imap::Flags::Seen));
221 mail.setImportant(message.flags.contains(Imap::Flags::Flagged));
222
223 createOrModify(bufferType, remoteId, mail);
211 } 224 }
212 const auto content = message.msg->encodedContent(); 225 commitSync();
213 file.write(content); 226 const auto elapsed = time->elapsed();
214 mail.setMimeMessagePath(filePath); 227 Log() << "Synchronized " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
215 //FIXME Not sure if these are the actual flags
216 mail.setUnread(!message.flags.contains(Imap::Flags::Seen));
217 mail.setImportant(message.flags.contains(Imap::Flags::Flagged));
218
219 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail);
220 } 228 }
221 mSynchronizerQueue.commit();
222 const auto elapsed = time->elapsed();
223 Log() << "Synchronized " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
224}
225
226void ImapResource::synchronizeRemovals(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QSet<qint64> &messages)
227{
228 auto time = QSharedPointer<QTime>::create();
229 time->start();
230 const QByteArray bufferType = ENTITY_TYPE_MAIL;
231
232 Trace() << "Finding removed mail.";
233
234 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction);
235 229
236 int count = 0; 230 void synchronizeRemovals(const QString &path, const QSet<qint64> &messages)
237 auto property = Sink::ApplicationDomain::Mail::Folder::name; 231 {
238 scanForRemovals(transaction, synchronizationTransaction, bufferType, 232 auto time = QSharedPointer<QTime>::create();
239 [&](const std::function<void(const QByteArray &)> &callback) { 233 time->start();
240 Index index(bufferType + ".index." + property, transaction); 234 const QByteArray bufferType = ENTITY_TYPE_MAIL;
241 index.lookup(folderLocalId, [&](const QByteArray &sinkId) { 235
242 callback(sinkId); 236 Trace() << "Finding removed mail.";
237
238 const auto folderLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8());
239
240 int count = 0;
241 auto property = Sink::ApplicationDomain::Mail::Folder::name;
242 scanForRemovals(bufferType,
243 [&](const std::function<void(const QByteArray &)> &callback) {
244 Index index(bufferType + ".index." + property, transaction());
245 index.lookup(folderLocalId, [&](const QByteArray &sinkId) {
246 callback(sinkId);
247 },
248 [&](const Index::Error &error) {
249 Warning() << "Error in index: " << error.message << property;
250 });
243 }, 251 },
244 [&](const Index::Error &error) { 252 [messages, path, &count](const QByteArray &remoteId) -> bool {
245 Warning() << "Error in index: " << error.message << property; 253 if (messages.contains(uidFromMessageRemoteId(remoteId))) {
246 }); 254 return true;
247 }, 255 }
248 [messages, path, &count](const QByteArray &remoteId) -> bool { 256 count++;
249 if (messages.contains(uidFromMessageRemoteId(remoteId))) { 257 return false;
250 return true;
251 } 258 }
252 count++; 259 );
253 return false;
254 }
255 );
256
257 const auto elapsed = time->elapsed();
258 Log() << "Removed " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
259}
260 260
261KAsync::Job<void> ImapResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore) 261 const auto elapsed = time->elapsed();
262{ 262 Log() << "Removed " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
263 Log() << " Synchronizing"; 263 }
264 return KAsync::start<void>([this, &mainStore, &synchronizationStore](KAsync::Future<void> future) {
265 ImapServerProxy imap(mServer, mPort);
266 auto loginFuture = imap.login(mUser, mPassword).exec();
267 loginFuture.waitForFinished();
268 if (loginFuture.errorCode()) {
269 Warning() << "Login failed.";
270 future.setError(1, "Login failed");
271 return;
272 } else {
273 Trace() << "Login was successful";
274 }
275 264
276 QVector<Folder> folderList; 265 KAsync::Job<void> synchronizeWithSource() Q_DECL_OVERRIDE
277 auto folderFuture = imap.fetchFolders([this, &imap, &mainStore, &synchronizationStore, &folderList](const QVector<Folder> &folders) { 266 {
278 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly); 267 Log() << " Synchronizing";
279 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite); 268 return KAsync::start<void>([this](KAsync::Future<void> future) {
280 synchronizeFolders(folders, transaction, syncTransaction); 269 ImapServerProxy imap(mServer, mPort);
281 transaction.commit(); 270 auto loginFuture = imap.login(mUser, mPassword).exec();
282 syncTransaction.commit(); 271 loginFuture.waitForFinished();
283 folderList << folders; 272 if (loginFuture.errorCode()) {
273 Warning() << "Login failed.";
274 future.setError(1, "Login failed");
275 return;
276 } else {
277 Trace() << "Login was successful";
278 }
284 279
285 }); 280 QVector<Folder> folderList;
286 folderFuture.waitForFinished(); 281 auto folderFuture = imap.fetchFolders([this, &imap, &folderList](const QVector<Folder> &folders) {
287 if (folderFuture.errorCode()) { 282 synchronizeFolders(folders);
288 Warning() << "Folder sync failed."; 283 commit();
289 future.setError(1, "Folder list sync failed"); 284 commitSync();
290 return; 285 folderList << folders;
291 } else {
292 Trace() << "Folder sync was successful";
293 }
294 286
295 for (const auto &folder : folderList) {
296 // auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
297 // auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadOnly);
298
299 //TODO load entity to read sync settings should we have some (if the folder is existing already)
300 //Note that this will not work if we change any of those settings in the pipeline
301 //
302 // auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_FOLDER);
303 // const auto sinkId = resolveRemoteId(ENTITY_TYPE_FOLDER, folder.toUtf8(), syncTransaction);
304 // const auto found = mainDatabase.contains(sinkId);
305 // if (found) {
306 // if (auto current = getLatest(mainDatabase, sinkId, mFolderAdaptorFactory)) {
307 //
308 // }
309 // }
310
311 // transaction.commit();
312 // syncTransaction.commit();
313
314 QSet<qint64> uids;
315 auto messagesFuture = imap.fetchMessages(folder, [this, &mainStore, &synchronizationStore, folder, &uids](const QVector<Message> &messages) {
316 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
317 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
318 Trace() << "Synchronizing mails" << folder.normalizedPath();
319 for (const auto &msg : messages) {
320 uids << msg.uid;
321 }
322 synchronizeMails(transaction, syncTransaction, folder.normalizedPath(), messages);
323 transaction.commit();
324 syncTransaction.commit();
325 }); 287 });
326 messagesFuture.waitForFinished(); 288 folderFuture.waitForFinished();
327 if (messagesFuture.errorCode()) { 289 if (folderFuture.errorCode()) {
328 future.setError(1, "Folder sync failed: " + folder.normalizedPath()); 290 Warning() << "Folder sync failed.";
291 future.setError(1, "Folder list sync failed");
329 return; 292 return;
293 } else {
294 Trace() << "Folder sync was successful";
330 } 295 }
331 //Remove what there is to remove
332 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
333 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
334 synchronizeRemovals(transaction, syncTransaction, folder.normalizedPath(), uids);
335 transaction.commit();
336 syncTransaction.commit();
337 Trace() << "Folder synchronized: " << folder.normalizedPath();
338 }
339 296
340 Log() << "Done Synchronizing"; 297 for (const auto &folder : folderList) {
341 future.setFinished(); 298 // auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
342 }); 299 // auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadOnly);
343} 300
301 //TODO load entity to read sync settings should we have some (if the folder is existing already)
302 //Note that this will not work if we change any of those settings in the pipeline
303 //
304 // auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_FOLDER);
305 // const auto sinkId = resolveRemoteId(ENTITY_TYPE_FOLDER, folder.toUtf8(), syncTransaction);
306 // const auto found = mainDatabase.contains(sinkId);
307 // if (found) {
308 // if (auto current = getLatest(mainDatabase, sinkId, mFolderAdaptorFactory)) {
309 //
310 // }
311 // }
312
313 // transaction.commit();
314 // syncTransaction.commit();
315
316 QSet<qint64> uids;
317 auto messagesFuture = imap.fetchMessages(folder, [this, folder, &uids](const QVector<Message> &messages) {
318 Trace() << "Synchronizing mails" << folder.normalizedPath();
319 for (const auto &msg : messages) {
320 uids << msg.uid;
321 }
322 synchronizeMails(folder.normalizedPath(), messages);
323 commit();
324 commitSync();
325 });
326 messagesFuture.waitForFinished();
327 if (messagesFuture.errorCode()) {
328 future.setError(1, "Folder sync failed: " + folder.normalizedPath());
329 return;
330 }
331 //Remove what there is to remove
332 synchronizeRemovals(folder.normalizedPath(), uids);
333 commit();
334 commitSync();
335 Trace() << "Folder synchronized: " << folder.normalizedPath();
336 }
337
338 Log() << "Done Synchronizing";
339 future.setFinished();
340 });
341 }
344 342
345KAsync::Job<void> ImapResource::replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value) 343public:
344 QString mServer;
345 int mPort;
346 QString mUser;
347 QString mPassword;
348 QByteArray mResourceInstanceIdentifier;
349};
350
351ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline)
352 : Sink::GenericResource(PLUGIN_NAME, instanceIdentifier, pipeline)
346{ 353{
347 //TODO implement 354 auto config = ResourceConfig::getConfiguration(instanceIdentifier);
348 return KAsync::null<void>(); 355 mServer = config.value("server").toString();
356 mPort = config.value("port").toInt();
357 mUser = config.value("user").toString();
358 mPassword = config.value("password").toString();
359
360 auto synchronizer = QSharedPointer<ImapSynchronizer>::create(PLUGIN_NAME, instanceIdentifier);
361 synchronizer->mServer = mServer;
362 synchronizer->mPort = mPort;
363 synchronizer->mUser = mUser;
364 synchronizer->mPassword = mPassword;
365 synchronizer->mResourceInstanceIdentifier = instanceIdentifier;
366 setupSynchronizer(synchronizer);
367 auto changereplay = QSharedPointer<Sink::NullChangeReplay>::create();
368 // auto changereplay = QSharedPointer<ImapWriteback>::create(PLUGIN_NAME, instanceIdentifier);
369 // changereplay->mServer = mServer;
370 // changereplay->mPort = mPort;
371 // changereplay->mUser = mUser;
372 // changereplay->mPassword = mPassword;
373 setupChangereplay(changereplay);
374
375 setupPreprocessors(ENTITY_TYPE_MAIL, QVector<Sink::Preprocessor*>() << new MailPropertyExtractor << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>);
376 setupPreprocessors(ENTITY_TYPE_FOLDER, QVector<Sink::Preprocessor*>() << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>);
349} 377}
350 378
351void ImapResource::removeFromDisk(const QByteArray &instanceIdentifier) 379void ImapResource::removeFromDisk(const QByteArray &instanceIdentifier)
@@ -356,7 +384,98 @@ void ImapResource::removeFromDisk(const QByteArray &instanceIdentifier)
356 384
357KAsync::Job<void> ImapResource::inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue) 385KAsync::Job<void> ImapResource::inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue)
358{ 386{
359 //TODO 387 auto synchronizationStore = QSharedPointer<Sink::Storage>::create(Sink::storageLocation(), mResourceInstanceIdentifier + ".synchronization", Sink::Storage::ReadOnly);
388 auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::ReadOnly);
389
390 auto mainStore = QSharedPointer<Sink::Storage>::create(Sink::storageLocation(), mResourceInstanceIdentifier, Sink::Storage::ReadOnly);
391 auto transaction = mainStore->createTransaction(Sink::Storage::ReadOnly);
392
393 auto entityStore = QSharedPointer<Sink::EntityStore>::create(mResourceType, mResourceInstanceIdentifier, transaction);
394 auto syncStore = QSharedPointer<Sink::RemoteIdMap>::create(synchronizationTransaction);
395
396 Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue;
397
398 // if (domainType == ENTITY_TYPE_MAIL) {
399 // const auto mail = entityStore->read<Sink::ApplicationDomain::Mail>(entityId);
400 // const auto folder = entityStore->read<Sink::ApplicationDomain::Folder>(mail.getFolder());
401 // const auto folderRemoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder());
402 // const auto mailRemoteId = syncStore->resolveLocalId(ENTITY_TYPE_MAIL, mail.identifier());
403 // // const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath());
404 // ImapServerProxy imap(mServer, mPort);
405 // imap.login(mUser, mPassword).exec().waitForFinished();
406 // imap.select(folderRemoteId).exec().waitForFinished();
407 // KIMAP::ImapSet set;
408 // set.add(mailRemoteId.toLongLong());
409 // KIMAP::FetchJob::FetchScope scope;
410 // scope.mode = KIMAP::FetchJob::FetchScope::Full;
411 // imap.fetch(set, scope, [](const QVector<Imap::Message> &messages) {
412 //
413 // }).exec().waitForFinished();
414 //
415 // if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) {
416 // if (property == "unread") {
417 // // const auto flags = KPIM::Maildir::readEntryFlags(filePath.split('/').last());
418 // // if (expectedValue.toBool() && (flags & KPIM::Maildir::Seen)) {
419 // // return KAsync::error<void>(1, "Expected unread but couldn't find it.");
420 // // }
421 // // if (!expectedValue.toBool() && !(flags & KPIM::Maildir::Seen)) {
422 // // return KAsync::error<void>(1, "Expected read but couldn't find it.");
423 // // }
424 // return KAsync::null<void>();
425 // }
426 // if (property == "subject") {
427 // // KMime::Message *msg = new KMime::Message;
428 // // msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(filePath)));
429 // // msg->parse();
430 // //
431 // // if (msg->subject(true)->asUnicodeString() != expectedValue.toString()) {
432 // // return KAsync::error<void>(1, "Subject not as expected: " + msg->subject(true)->asUnicodeString());
433 // // }
434 // return KAsync::null<void>();
435 // }
436 // }
437 // if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
438 // // if (QFileInfo(filePath).exists() != expectedValue.toBool()) {
439 // // return KAsync::error<void>(1, "Wrong file existence: " + filePath);
440 // // }
441 // }
442 // }
443 // if (domainType == ENTITY_TYPE_FOLDER) {
444 // const auto remoteId = syncStore->resolveLocalId(ENTITY_TYPE_FOLDER, entityId);
445 // const auto folder = entityStore->read<Sink::ApplicationDomain::Folder>(entityId);
446 //
447 // if (inspectionType == Sink::ResourceControl::Inspection::CacheIntegrityInspectionType) {
448 // // Warning() << "Inspecting cache integrity" << remoteId;
449 // // if (!QDir(remoteId).exists()) {
450 // // return KAsync::error<void>(1, "The directory is not existing: " + remoteId);
451 // // }
452 // //
453 // // int expectedCount = 0;
454 // // Index index("mail.index.folder", transaction);
455 // // index.lookup(entityId, [&](const QByteArray &sinkId) {
456 // // expectedCount++;
457 // // },
458 // // [&](const Index::Error &error) {
459 // // Warning() << "Error in index: " << error.message << property;
460 // // });
461 // //
462 // // QDir dir(remoteId + "/cur");
463 // // const QFileInfoList list = dir.entryInfoList(QDir::Files);
464 // // if (list.size() != expectedCount) {
465 // // return KAsync::error<void>(1, QString("Wrong number of files; found %1 instead of %2.").arg(list.size()).arg(expectedCount));
466 // // }
467 // // if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
468 // // if (!remoteId.endsWith(folder.getName().toUtf8())) {
469 // // return KAsync::error<void>(1, "Wrong folder name: " + remoteId);
470 // // }
471 // // //TODO we shouldn't use the remoteId here to figure out the path, it could be gone/changed already
472 // // if (QDir(remoteId).exists() != expectedValue.toBool()) {
473 // // return KAsync::error<void>(1, "Wrong folder existence: " + remoteId);
474 // // }
475 // // }
476 // }
477 //
478 // }
360 return KAsync::null<void>(); 479 return KAsync::null<void>();
361} 480}
362 481
@@ -377,3 +496,8 @@ void ImapResourceFactory::registerFacades(Sink::FacadeFactory &factory)
377 factory.registerFacade<Sink::ApplicationDomain::Folder, ImapResourceFolderFacade>(PLUGIN_NAME); 496 factory.registerFacade<Sink::ApplicationDomain::Folder, ImapResourceFolderFacade>(PLUGIN_NAME);
378} 497}
379 498
499void ImapResourceFactory::registerAdaptorFactories(Sink::AdaptorFactoryRegistry &registry)
500{
501 registry.registerFactory<Sink::ApplicationDomain::Mail, ImapMailAdaptorFactory>(PLUGIN_NAME);
502 registry.registerFactory<Sink::ApplicationDomain::Folder, ImapFolderAdaptorFactory>(PLUGIN_NAME);
503}