summaryrefslogtreecommitdiffstats
path: root/examples/maildirresource/maildirresource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/maildirresource/maildirresource.cpp')
-rw-r--r--examples/maildirresource/maildirresource.cpp82
1 files changed, 67 insertions, 15 deletions
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 33883a7..0ddd8f8 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -46,6 +46,9 @@
46#define ENTITY_TYPE_MAIL "mail" 46#define ENTITY_TYPE_MAIL "mail"
47#define ENTITY_TYPE_FOLDER "folder" 47#define ENTITY_TYPE_FOLDER "folder"
48 48
49#undef DEBUG_AREA
50#define DEBUG_AREA "resource.maildir"
51
49MaildirResource::MaildirResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline) 52MaildirResource::MaildirResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline)
50 : Sink::GenericResource(instanceIdentifier, pipeline), 53 : Sink::GenericResource(instanceIdentifier, pipeline),
51 mMailAdaptorFactory(QSharedPointer<MaildirMailAdaptorFactory>::create()), 54 mMailAdaptorFactory(QSharedPointer<MaildirMailAdaptorFactory>::create()),
@@ -103,7 +106,7 @@ void MaildirResource::synchronizeFolders(Sink::Storage::Transaction &transaction
103 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index, 106 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index,
104 //but we currently fail to iterate over all entries in an index it seems. 107 //but we currently fail to iterate over all entries in an index it seems.
105 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true); 108 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true);
106 auto mainDatabase = transaction.openDatabase(bufferType + ".main"); 109 auto mainDatabase = Sink::Storage::mainDatabase(transaction, bufferType);
107 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) { 110 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) {
108 callback(key); 111 callback(key);
109 return true; 112 return true;
@@ -132,6 +135,8 @@ void MaildirResource::synchronizeFolders(Sink::Storage::Transaction &transaction
132void MaildirResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path) 135void MaildirResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path)
133{ 136{
134 Trace() << "Synchronizing mails" << path; 137 Trace() << "Synchronizing mails" << path;
138 auto time = QSharedPointer<QTime>::create();
139 time->start();
135 const QByteArray bufferType = ENTITY_TYPE_MAIL; 140 const QByteArray bufferType = ENTITY_TYPE_MAIL;
136 141
137 KPIM::Maildir maildir(path, true); 142 KPIM::Maildir maildir(path, true);
@@ -162,7 +167,9 @@ void MaildirResource::synchronizeMails(Sink::Storage::Transaction &transaction,
162 } 167 }
163 ); 168 );
164 169
170 int count = 0;
165 while (entryIterator->hasNext()) { 171 while (entryIterator->hasNext()) {
172 count++;
166 const QString filePath = QDir::fromNativeSeparators(entryIterator->next()); 173 const QString filePath = QDir::fromNativeSeparators(entryIterator->next());
167 const QString fileName = entryIterator->fileName(); 174 const QString fileName = entryIterator->fileName();
168 const auto remoteId = filePath.toUtf8(); 175 const auto remoteId = filePath.toUtf8();
@@ -172,6 +179,7 @@ void MaildirResource::synchronizeMails(Sink::Storage::Transaction &transaction,
172 msg->parse(); 179 msg->parse();
173 180
174 const auto flags = maildir.readEntryFlags(fileName); 181 const auto flags = maildir.readEntryFlags(fileName);
182 const auto maildirKey = maildir.getKeyFromFile(fileName);
175 183
176 Trace() << "Found a mail " << filePath << " : " << fileName << msg->subject(true)->asUnicodeString(); 184 Trace() << "Found a mail " << filePath << " : " << fileName << msg->subject(true)->asUnicodeString();
177 185
@@ -181,12 +189,16 @@ void MaildirResource::synchronizeMails(Sink::Storage::Transaction &transaction,
181 mail.setProperty("senderName", msg->from(true)->asUnicodeString()); 189 mail.setProperty("senderName", msg->from(true)->asUnicodeString());
182 mail.setProperty("date", msg->date(true)->dateTime()); 190 mail.setProperty("date", msg->date(true)->dateTime());
183 mail.setProperty("folder", folderLocalId); 191 mail.setProperty("folder", folderLocalId);
184 mail.setProperty("mimeMessage", filePath); 192 //We only store the directory path + key, so we facade can add the changing bits (flags)
193 mail.setProperty("mimeMessage", KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey);
185 mail.setProperty("unread", !flags.testFlag(KPIM::Maildir::Seen)); 194 mail.setProperty("unread", !flags.testFlag(KPIM::Maildir::Seen));
186 mail.setProperty("important", flags.testFlag(KPIM::Maildir::Flagged)); 195 mail.setProperty("important", flags.testFlag(KPIM::Maildir::Flagged));
187 196
188 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail); 197 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail);
189 } 198 }
199 const auto elapsed = time->elapsed();
200 Trace() << "Synchronized " << count << " mails in " << listingPath << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
201
190} 202}
191 203
192KAsync::Job<void> MaildirResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore) 204KAsync::Job<void> MaildirResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore)
@@ -216,7 +228,7 @@ KAsync::Job<void> MaildirResource::replay(Sink::Storage &synchronizationStore, c
216 228
217 Trace() << "Replaying " << key << type; 229 Trace() << "Replaying " << key << type;
218 if (type == ENTITY_TYPE_FOLDER) { 230 if (type == ENTITY_TYPE_FOLDER) {
219 Sink::EntityBuffer buffer(value.data(), value.size()); 231 Sink::EntityBuffer buffer(value);
220 const Sink::Entity &entity = buffer.entity(); 232 const Sink::Entity &entity = buffer.entity();
221 const auto metadataBuffer = Sink::EntityBuffer::readBuffer<Sink::Metadata>(entity.metadata()); 233 const auto metadataBuffer = Sink::EntityBuffer::readBuffer<Sink::Metadata>(entity.metadata());
222 if (metadataBuffer && !metadataBuffer->replayToSource()) { 234 if (metadataBuffer && !metadataBuffer->replayToSource()) {
@@ -248,7 +260,7 @@ KAsync::Job<void> MaildirResource::replay(Sink::Storage &synchronizationStore, c
248 Warning() << "Unkown operation" << operation; 260 Warning() << "Unkown operation" << operation;
249 } 261 }
250 } else if (type == ENTITY_TYPE_MAIL) { 262 } else if (type == ENTITY_TYPE_MAIL) {
251 Sink::EntityBuffer buffer(value.data(), value.size()); 263 Sink::EntityBuffer buffer(value);
252 const Sink::Entity &entity = buffer.entity(); 264 const Sink::Entity &entity = buffer.entity();
253 const auto metadataBuffer = Sink::EntityBuffer::readBuffer<Sink::Metadata>(entity.metadata()); 265 const auto metadataBuffer = Sink::EntityBuffer::readBuffer<Sink::Metadata>(entity.metadata());
254 if (metadataBuffer && !metadataBuffer->replayToSource()) { 266 if (metadataBuffer && !metadataBuffer->replayToSource()) {
@@ -268,10 +280,19 @@ KAsync::Job<void> MaildirResource::replay(Sink::Storage &synchronizationStore, c
268 } 280 }
269 const auto parentFolderPath = parentFolderRemoteId; 281 const auto parentFolderPath = parentFolderRemoteId;
270 KPIM::Maildir maildir(parentFolderPath, false); 282 KPIM::Maildir maildir(parentFolderPath, false);
271 //FIXME assemble the MIME message 283 if (!maildir.isValid(true)) {
272 const auto id = maildir.addEntry("foobar"); 284 return KAsync::error<void>(1, "Invalid folder " + parentFolderPath);
273 Trace() << "Creating a new mail: " << id; 285 }
274 recordRemoteId(ENTITY_TYPE_MAIL, mail.identifier(), id.toUtf8(), synchronizationTransaction); 286 //FIXME move the mime message from the mimeMessage property to the proper place.
287 Trace() << "Creating a new mail.";
288 const auto remoteId = maildir.addEntry("foobar");
289 if (remoteId.isEmpty()) {
290 Warning() << "Failed to create mail: " << remoteId;
291 return KAsync::error<void>(1, "Failed to create mail.");
292 } else {
293 Trace() << "Mail created: " << remoteId;
294 recordRemoteId(ENTITY_TYPE_MAIL, mail.identifier(), remoteId.toUtf8(), synchronizationTransaction);
295 }
275 } else if (operation == Sink::Operation_Removal) { 296 } else if (operation == Sink::Operation_Removal) {
276 const auto uid = Sink::Storage::uidFromKey(key); 297 const auto uid = Sink::Storage::uidFromKey(key);
277 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction); 298 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction);
@@ -279,7 +300,26 @@ KAsync::Job<void> MaildirResource::replay(Sink::Storage &synchronizationStore, c
279 QFile::remove(remoteId); 300 QFile::remove(remoteId);
280 removeRemoteId(ENTITY_TYPE_MAIL, uid, remoteId, synchronizationTransaction); 301 removeRemoteId(ENTITY_TYPE_MAIL, uid, remoteId, synchronizationTransaction);
281 } else if (operation == Sink::Operation_Modification) { 302 } else if (operation == Sink::Operation_Modification) {
282 Warning() << "Mail modifications are not implemented"; 303 const auto uid = Sink::Storage::uidFromKey(key);
304 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, uid, synchronizationTransaction);
305 Trace() << "Modifying a mail: " << remoteId;
306 auto parts = remoteId.split('/');
307 const auto filename = parts.takeLast(); //filename
308 parts.removeLast(); //cur/new folder
309 auto maildirPath = parts.join('/');
310
311 KPIM::Maildir maildir(maildirPath, false);
312
313 const Sink::ApplicationDomain::Mail mail(mResourceInstanceIdentifier, Sink::Storage::uidFromKey(key), revision, mMailAdaptorFactory->createAdaptor(entity));
314
315 //get flags from
316 KPIM::Maildir::Flags flags;
317 if (!mail.getProperty("unread").toBool()) {
318 flags |= KPIM::Maildir::Seen;
319 }
320
321 auto newRemoteId = maildir.changeEntryFlags(filename, flags);
322 updateRemoteId(ENTITY_TYPE_MAIL, uid, QString(maildirPath + "/cur/" + newRemoteId).toUtf8(), synchronizationTransaction);
283 } else { 323 } else {
284 Warning() << "Unkown operation" << operation; 324 Warning() << "Unkown operation" << operation;
285 } 325 }
@@ -299,20 +339,32 @@ KAsync::Job<void> MaildirResource::inspect(int inspectionType, const QByteArray
299 auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::ReadOnly); 339 auto synchronizationTransaction = synchronizationStore->createTransaction(Sink::Storage::ReadOnly);
300 Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue; 340 Trace() << "Inspecting " << inspectionType << domainType << entityId << property << expectedValue;
301 if (domainType == ENTITY_TYPE_MAIL) { 341 if (domainType == ENTITY_TYPE_MAIL) {
302 if (inspectionType == Sink::Resources::Inspection::PropertyInspectionType) { 342 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) {
303 if (property == "unread") { 343 if (property == "unread") {
304 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction); 344 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction);
305 const auto flags = KPIM::Maildir::readEntryFlags(remoteId.split('/').last()); 345 const auto flags = KPIM::Maildir::readEntryFlags(remoteId.split('/').last());
306 if (expectedValue.toBool() && !(flags & KPIM::Maildir::Seen)) { 346 if (expectedValue.toBool() && (flags & KPIM::Maildir::Seen)) {
307 return KAsync::error<void>(1, "Expected seen but couldn't find it."); 347 return KAsync::error<void>(1, "Expected unread but couldn't find it.");
308 } 348 }
309 if (!expectedValue.toBool() && (flags & KPIM::Maildir::Seen)) { 349 if (!expectedValue.toBool() && !(flags & KPIM::Maildir::Seen)) {
310 return KAsync::error<void>(1, "Expected seen but couldn't find it."); 350 return KAsync::error<void>(1, "Expected read but couldn't find it.");
351 }
352 return KAsync::null<void>();
353 }
354 if (property == "subject") {
355 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction);
356
357 KMime::Message *msg = new KMime::Message;
358 msg->setHead(KMime::CRLFtoLF(KPIM::Maildir::readEntryHeadersFromFile(remoteId)));
359 msg->parse();
360
361 if (msg->subject(true)->asUnicodeString() != expectedValue.toString()) {
362 return KAsync::error<void>(1, "Subject not as expected: " + msg->subject(true)->asUnicodeString());
311 } 363 }
312 return KAsync::null<void>(); 364 return KAsync::null<void>();
313 } 365 }
314 } 366 }
315 if (inspectionType == Sink::Resources::Inspection::ExistenceInspectionType) { 367 if (inspectionType == Sink::ResourceControl::Inspection::ExistenceInspectionType) {
316 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction); 368 const auto remoteId = resolveLocalId(ENTITY_TYPE_MAIL, entityId, synchronizationTransaction);
317 if (QFileInfo(remoteId).exists() != expectedValue.toBool()) { 369 if (QFileInfo(remoteId).exists() != expectedValue.toBool()) {
318 return KAsync::error<void>(1, "Wrong file existence: " + remoteId); 370 return KAsync::error<void>(1, "Wrong file existence: " + remoteId);