diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-06-08 17:08:36 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-06-08 17:08:36 +0200 |
commit | 6c85a913c06661aa3f095cdf4bf278d5d65b6930 (patch) | |
tree | ed96474a8f3cf682ecbfb68c5135e23077177dae | |
parent | 16d4e7fcbe4e7817b4cbe4f7d2644520e1af1658 (diff) | |
download | sink-6c85a913c06661aa3f095cdf4bf278d5d65b6930.tar.gz sink-6c85a913c06661aa3f095cdf4bf278d5d65b6930.zip |
Less hardcoded assumptions and a new RID scheme
By using (folder local id:imap uid) for mails, we don't have to change
mail rid's on folder renames.
-rw-r--r-- | examples/imapresource/imapresource.cpp | 151 | ||||
-rw-r--r-- | examples/imapresource/imapserverproxy.cpp | 79 | ||||
-rw-r--r-- | examples/imapresource/imapserverproxy.h | 22 |
3 files changed, 163 insertions, 89 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp index 6168fa4..fee479a 100644 --- a/examples/imapresource/imapresource.cpp +++ b/examples/imapresource/imapresource.cpp | |||
@@ -103,6 +103,31 @@ public: | |||
103 | 103 | ||
104 | }; | 104 | }; |
105 | 105 | ||
106 | static qint64 uidFromMailRid(const QByteArray &remoteId) | ||
107 | { | ||
108 | auto ridParts = remoteId.split(':'); | ||
109 | Q_ASSERT(ridParts.size() == 2); | ||
110 | return ridParts.last().toLongLong(); | ||
111 | } | ||
112 | |||
113 | static QByteArray folderIdFromMailRid(const QByteArray &remoteId) | ||
114 | { | ||
115 | auto ridParts = remoteId.split(':'); | ||
116 | Q_ASSERT(ridParts.size() == 2); | ||
117 | return ridParts.first(); | ||
118 | } | ||
119 | |||
120 | static QByteArray assembleMailRid(const QByteArray &folderLocalId, qint64 imapUid) | ||
121 | { | ||
122 | return folderLocalId + ':' + QByteArray::number(imapUid); | ||
123 | } | ||
124 | |||
125 | static QByteArray assembleMailRid(const ApplicationDomain::Mail &mail, qint64 imapUid) | ||
126 | { | ||
127 | return assembleMailRid(mail.getFolder(), imapUid); | ||
128 | } | ||
129 | |||
130 | |||
106 | class ImapSynchronizer : public Sink::Synchronizer { | 131 | class ImapSynchronizer : public Sink::Synchronizer { |
107 | public: | 132 | public: |
108 | ImapSynchronizer(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier) | 133 | ImapSynchronizer(const QByteArray &resourceType, const QByteArray &resourceInstanceIdentifier) |
@@ -111,18 +136,17 @@ public: | |||
111 | 136 | ||
112 | } | 137 | } |
113 | 138 | ||
114 | QByteArray createFolder(const QString &folderPath, const QByteArray &icon) | 139 | QByteArray createFolder(const QString &folderName, const QString &folderPath, const QString &parentFolderRid, const QByteArray &icon) |
115 | { | 140 | { |
116 | auto remoteId = folderPath.toUtf8(); | 141 | Trace() << "Creating folder: " << folderName << parentFolderRid; |
117 | auto bufferType = ENTITY_TYPE_FOLDER; | 142 | const auto remoteId = folderPath.toUtf8(); |
143 | const auto bufferType = ENTITY_TYPE_FOLDER; | ||
118 | Sink::ApplicationDomain::Folder folder; | 144 | Sink::ApplicationDomain::Folder folder; |
119 | auto folderPathParts = folderPath.split('/'); | 145 | folder.setProperty("name", folderName); |
120 | const auto name = folderPathParts.takeLast(); | ||
121 | folder.setProperty("name", name); | ||
122 | folder.setProperty("icon", icon); | 146 | folder.setProperty("icon", icon); |
123 | 147 | ||
124 | if (!folderPathParts.isEmpty()) { | 148 | if (!parentFolderRid.isEmpty()) { |
125 | folder.setProperty("parent", syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, folderPathParts.join('/').toUtf8())); | 149 | folder.setProperty("parent", syncStore().resolveRemoteId(ENTITY_TYPE_FOLDER, parentFolderRid.toUtf8())); |
126 | } | 150 | } |
127 | createOrModify(bufferType, remoteId, folder); | 151 | createOrModify(bufferType, remoteId, folder); |
128 | return remoteId; | 152 | return remoteId; |
@@ -146,9 +170,9 @@ public: | |||
146 | }); | 170 | }); |
147 | }, | 171 | }, |
148 | [&folderList](const QByteArray &remoteId) -> bool { | 172 | [&folderList](const QByteArray &remoteId) -> bool { |
149 | //folderList.contains(remoteId) | 173 | // folderList.contains(remoteId) |
150 | for (const auto folderPath : folderList) { | 174 | for (const auto folderPath : folderList) { |
151 | if (folderPath.pathParts.join('/') == remoteId) { | 175 | if (folderPath.path == remoteId) { |
152 | return true; | 176 | return true; |
153 | } | 177 | } |
154 | } | 178 | } |
@@ -156,21 +180,11 @@ public: | |||
156 | } | 180 | } |
157 | ); | 181 | ); |
158 | 182 | ||
159 | for (const auto folderPath : folderList) { | 183 | for (const auto &f : folderList) { |
160 | createFolder(folderPath.pathParts.join('/'), "folder"); | 184 | createFolder(f.pathParts.last(), f.path, f.parentPath(), "folder"); |
161 | } | 185 | } |
162 | } | 186 | } |
163 | 187 | ||
164 | static QByteArray remoteIdForMessage(const QString &path, qint64 uid) | ||
165 | { | ||
166 | return path.toUtf8() + "/" + QByteArray::number(uid); | ||
167 | } | ||
168 | |||
169 | static qint64 uidFromMessageRemoteId(const QByteArray &remoteId) | ||
170 | { | ||
171 | return remoteId.split('/').last().toLongLong(); | ||
172 | } | ||
173 | |||
174 | void synchronizeMails(const QString &path, const QVector<Message> &messages) | 188 | void synchronizeMails(const QString &path, const QVector<Message> &messages) |
175 | { | 189 | { |
176 | auto time = QSharedPointer<QTime>::create(); | 190 | auto time = QSharedPointer<QTime>::create(); |
@@ -184,7 +198,7 @@ public: | |||
184 | int count = 0; | 198 | int count = 0; |
185 | for (const auto &message : messages) { | 199 | for (const auto &message : messages) { |
186 | count++; | 200 | count++; |
187 | const auto remoteId = path.toUtf8() + "/" + QByteArray::number(message.uid); | 201 | const auto remoteId = assembleMailRid(folderLocalId, message.uid); |
188 | 202 | ||
189 | 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; |
190 | 204 | ||
@@ -237,7 +251,7 @@ public: | |||
237 | }); | 251 | }); |
238 | }, | 252 | }, |
239 | [messages, path, &count](const QByteArray &remoteId) -> bool { | 253 | [messages, path, &count](const QByteArray &remoteId) -> bool { |
240 | if (messages.contains(uidFromMessageRemoteId(remoteId))) { | 254 | if (messages.contains(uidFromMailRid(remoteId))) { |
241 | return true; | 255 | return true; |
242 | } | 256 | } |
243 | count++; | 257 | count++; |
@@ -330,8 +344,7 @@ public: | |||
330 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | 344 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); |
331 | auto login = imap->login(mUser, mPassword); | 345 | auto login = imap->login(mUser, mPassword); |
332 | if (operation == Sink::Operation_Creation) { | 346 | if (operation == Sink::Operation_Creation) { |
333 | auto parentRemoteId = syncStore().resolveLocalId("folder", mail.getFolder()); | 347 | QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); |
334 | QString mailbox = parentRemoteId; | ||
335 | QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); | 348 | QByteArray content = KMime::LFtoCRLF(mail.getMimeMessage()); |
336 | QByteArrayList flags; | 349 | QByteArrayList flags; |
337 | if (!mail.getUnread()) { | 350 | if (!mail.getUnread()) { |
@@ -343,19 +356,18 @@ public: | |||
343 | QDateTime internalDate = mail.getDate(); | 356 | QDateTime internalDate = mail.getDate(); |
344 | auto rid = QSharedPointer<QByteArray>::create(); | 357 | auto rid = QSharedPointer<QByteArray>::create(); |
345 | return login.then(imap->append(mailbox, content, flags, internalDate)) | 358 | return login.then(imap->append(mailbox, content, flags, internalDate)) |
346 | .then<void, qint64>([imap, mailbox, rid](qint64 uid) { | 359 | .then<void, qint64>([imap, mailbox, rid, mail](qint64 uid) { |
347 | const auto remoteId = mailbox.toUtf8() + "/" + QByteArray::number(uid); | 360 | const auto remoteId = assembleMailRid(mail, uid); |
348 | //FIXME this get's called after the final error ahndler? WTF? | 361 | //FIXME this get's called after the final error handler? WTF? |
349 | Trace() << "Finished creating a new mail: " << remoteId; | 362 | Trace() << "Finished creating a new mail: " << remoteId; |
350 | *rid = remoteId; | 363 | *rid = remoteId; |
351 | }).then<QByteArray>([rid, imap]() { //FIXME fix KJob so we don't need this extra clause | 364 | }).then<QByteArray>([rid, imap]() { //FIXME fix KJob so we don't need this extra clause |
352 | return *rid; | 365 | return *rid; |
353 | }); | 366 | }); |
354 | } else if (operation == Sink::Operation_Removal) { | 367 | } else if (operation == Sink::Operation_Removal) { |
355 | auto ridParts = oldRemoteId.split('/'); | 368 | const auto folderId = folderIdFromMailRid(oldRemoteId); |
356 | auto uid = ridParts.takeLast().toLongLong(); | 369 | const QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folderId); |
357 | //FIXME don't hardcode the separator | 370 | const auto uid = uidFromMailRid(oldRemoteId); |
358 | auto mailbox = ridParts.join('.'); | ||
359 | Trace() << "Removing a mail: " << oldRemoteId << "in the mailbox: " << mailbox; | 371 | Trace() << "Removing a mail: " << oldRemoteId << "in the mailbox: " << mailbox; |
360 | KIMAP::ImapSet set; | 372 | KIMAP::ImapSet set; |
361 | set.add(uid); | 373 | set.add(uid); |
@@ -365,10 +377,9 @@ public: | |||
365 | return QByteArray(); | 377 | return QByteArray(); |
366 | }); | 378 | }); |
367 | } else if (operation == Sink::Operation_Modification) { | 379 | } else if (operation == Sink::Operation_Modification) { |
368 | auto ridParts = oldRemoteId.split('/'); | 380 | const QString mailbox = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, mail.getFolder()); |
369 | auto uid = ridParts.takeLast().toLongLong(); | 381 | const auto uid = uidFromMailRid(oldRemoteId); |
370 | //FIXME don't hardcode the separator | 382 | |
371 | auto mailbox = ridParts.join('.'); | ||
372 | Trace() << "Modifying a mail: " << oldRemoteId << " in the mailbox: " << mailbox << changedProperties; | 383 | Trace() << "Modifying a mail: " << oldRemoteId << " in the mailbox: " << mailbox << changedProperties; |
373 | 384 | ||
374 | QByteArrayList flags; | 385 | QByteArrayList flags; |
@@ -387,14 +398,14 @@ public: | |||
387 | KIMAP::ImapSet set; | 398 | KIMAP::ImapSet set; |
388 | set.add(uid); | 399 | set.add(uid); |
389 | return login.then(imap->append(mailbox, content, flags, internalDate)) | 400 | return login.then(imap->append(mailbox, content, flags, internalDate)) |
390 | .then<void, qint64>([imap, mailbox, rid](qint64 uid) { | 401 | .then<void, qint64>([imap, mailbox, rid, mail](qint64 uid) { |
391 | const auto remoteId = mailbox + "/" + QByteArray::number(uid); | 402 | const auto remoteId = assembleMailRid(mail, uid); |
392 | Trace() << "Finished creating a modified mail: " << remoteId; | 403 | Trace() << "Finished creating a modified mail: " << remoteId; |
393 | *rid = remoteId; | 404 | *rid = remoteId; |
394 | }) | 405 | }) |
395 | .then(imap->remove(mailbox, set)) | 406 | .then(imap->remove(mailbox, set)) |
396 | .then<QByteArray>([rid, imap]() { | 407 | .then<QByteArray>([rid, imap]() { |
397 | return *rid; | 408 | return *rid; |
398 | }); | 409 | }); |
399 | } else { | 410 | } else { |
400 | KIMAP::ImapSet set; | 411 | KIMAP::ImapSet set; |
@@ -402,42 +413,34 @@ public: | |||
402 | return login.then(imap->select(mailbox)) | 413 | return login.then(imap->select(mailbox)) |
403 | .then(imap->storeFlags(set, flags)) | 414 | .then(imap->storeFlags(set, flags)) |
404 | .then<void, qint64>([imap, mailbox](qint64 uid) { | 415 | .then<void, qint64>([imap, mailbox](qint64 uid) { |
405 | const auto remoteId = mailbox + "/" + QByteArray::number(uid); | 416 | Trace() << "Finished modifying mail: " << uid; |
406 | Trace() << "Finished modifying mail: " << remoteId; | ||
407 | }) | 417 | }) |
408 | .then<QByteArray>([oldRemoteId, imap]() { | 418 | .then<QByteArray>([oldRemoteId, imap]() { |
409 | return oldRemoteId; | 419 | return oldRemoteId; |
410 | }); | 420 | }); |
411 | } | 421 | } |
412 | } | 422 | } |
413 | return KAsync::null<QByteArray>(); | 423 | return KAsync::null<QByteArray>(); |
414 | } | 424 | } |
415 | 425 | ||
416 | QString buildPath(const ApplicationDomain::Folder &folder) | ||
417 | { | ||
418 | //Don't use entityStore in here, it can produce wrong results we're replaying an older revision than the latest one | ||
419 | QChar separator = '/'; | ||
420 | QString path; | ||
421 | auto parent = folder.getParent(); | ||
422 | if (!parent.isEmpty()) { | ||
423 | auto parentRemoteId = syncStore().resolveLocalId("folder", parent); | ||
424 | return parentRemoteId + separator.toLatin1() + folder.getName().toUtf8(); | ||
425 | } | ||
426 | return separator + folder.getName(); | ||
427 | } | ||
428 | |||
429 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE | 426 | KAsync::Job<QByteArray> replay(const ApplicationDomain::Folder &folder, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE |
430 | { | 427 | { |
431 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); | 428 | auto imap = QSharedPointer<ImapServerProxy>::create(mServer, mPort); |
432 | auto login = imap->login(mUser, mPassword); | 429 | auto login = imap->login(mUser, mPassword); |
433 | if (operation == Sink::Operation_Creation) { | 430 | if (operation == Sink::Operation_Creation) { |
434 | auto folderPath = buildPath(folder); | 431 | QString parentFolder; |
435 | auto imapPath = "INBOX" + folderPath.replace('/', '.'); | 432 | if (!folder.getParent().isEmpty()) { |
436 | Trace() << "Creating a new folder: " << imapPath; | 433 | parentFolder = syncStore().resolveLocalId(ENTITY_TYPE_FOLDER, folder.getParent()); |
437 | return login.then<void>(imap->create(imapPath)) | 434 | } |
438 | .then<QByteArray>([imapPath, imap]() { | 435 | Trace() << "Creating a new folder: " << parentFolder << folder.getName(); |
439 | Trace() << "Finished creating a new folder: " << imapPath; | 436 | auto rid = QSharedPointer<QByteArray>::create(); |
440 | return imapPath.toUtf8(); | 437 | return login.then<QString>(imap->createSubfolder(parentFolder, folder.getName())) |
438 | .then<void, QString>([imap, rid](const QString &createdFolder) { | ||
439 | Trace() << "Finished creating a new folder: " << createdFolder; | ||
440 | *rid = createdFolder.toUtf8(); | ||
441 | }) | ||
442 | .then<QByteArray>([rid](){ | ||
443 | return *rid; | ||
441 | }); | 444 | }); |
442 | } else if (operation == Sink::Operation_Removal) { | 445 | } else if (operation == Sink::Operation_Removal) { |
443 | Trace() << "Removing a folder: " << oldRemoteId; | 446 | Trace() << "Removing a folder: " << oldRemoteId; |
@@ -447,13 +450,15 @@ public: | |||
447 | return QByteArray(); | 450 | return QByteArray(); |
448 | }); | 451 | }); |
449 | } else if (operation == Sink::Operation_Modification) { | 452 | } else if (operation == Sink::Operation_Modification) { |
450 | auto newFolderPath = buildPath(folder); | 453 | Trace() << "Renaming a folder: " << oldRemoteId << folder.getName(); |
451 | auto newImapPath = "INBOX" + newFolderPath.replace('/', '.'); | 454 | auto rid = QSharedPointer<QByteArray>::create(); |
452 | Trace() << "Renaming a folder: " << oldRemoteId << newImapPath; | 455 | return login.then<QString>(imap->renameSubfolder(oldRemoteId, folder.getName())) |
453 | return login.then<void>(imap->rename(oldRemoteId, newImapPath)) | 456 | .then<void, QString>([imap, rid](const QString &createdFolder) { |
454 | .then<QByteArray>([newImapPath, imap]() { | 457 | Trace() << "Finished renaming a folder: " << createdFolder; |
455 | Trace() << "Finished renaming a folder: " << newImapPath; | 458 | *rid = createdFolder.toUtf8(); |
456 | return newImapPath.toUtf8(); | 459 | }) |
460 | .then<QByteArray>([rid](){ | ||
461 | return *rid; | ||
457 | }); | 462 | }); |
458 | } | 463 | } |
459 | return KAsync::null<QByteArray>(); | 464 | return KAsync::null<QByteArray>(); |
@@ -522,7 +527,7 @@ KAsync::Job<void> ImapResource::inspect(int inspectionType, const QByteArray &in | |||
522 | Warning() << "Missing remote id for folder or mail. " << mailRemoteId << folderRemoteId; | 527 | Warning() << "Missing remote id for folder or mail. " << mailRemoteId << folderRemoteId; |
523 | return KAsync::error<void>(); | 528 | return KAsync::error<void>(); |
524 | } | 529 | } |
525 | auto uid = mailRemoteId.split('/').last().toLongLong(); | 530 | const auto uid = uidFromMailRid(mailRemoteId); |
526 | Trace() << "Mail remote id: " << folderRemoteId << mailRemoteId << mail.identifier() << folder.identifier(); | 531 | Trace() << "Mail remote id: " << folderRemoteId << mailRemoteId << mail.identifier() << folder.identifier(); |
527 | 532 | ||
528 | KIMAP::ImapSet set; | 533 | KIMAP::ImapSet set; |
@@ -606,7 +611,7 @@ KAsync::Job<void> ImapResource::inspect(int inspectionType, const QByteArray &in | |||
606 | })) | 611 | })) |
607 | .then<void, KAsync::Job<void>>([imap, messageByUid, expectedCount]() { | 612 | .then<void, KAsync::Job<void>>([imap, messageByUid, expectedCount]() { |
608 | if (messageByUid->size() != expectedCount) { | 613 | if (messageByUid->size() != expectedCount) { |
609 | return KAsync::error<void>(1, QString("Wrong number of files; found %1 instead of %2.").arg(messageByUid->size()).arg(expectedCount)); | 614 | return KAsync::error<void>(1, QString("Wrong number of messages on the server; found %1 instead of %2.").arg(messageByUid->size()).arg(expectedCount)); |
610 | } | 615 | } |
611 | return KAsync::null<void>(); | 616 | return KAsync::null<void>(); |
612 | }); | 617 | }); |
diff --git a/examples/imapresource/imapserverproxy.cpp b/examples/imapresource/imapserverproxy.cpp index 8c12b6c..db68a53 100644 --- a/examples/imapresource/imapserverproxy.cpp +++ b/examples/imapresource/imapserverproxy.cpp | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <QDir> | 21 | #include <QDir> |
22 | #include <QFile> | 22 | #include <QFile> |
23 | #include <KIMAP/KIMAP/LoginJob> | 23 | #include <KIMAP/KIMAP/LoginJob> |
24 | #include <kimap/namespacejob.h> | ||
24 | #include <KIMAP/KIMAP/SelectJob> | 25 | #include <KIMAP/KIMAP/SelectJob> |
25 | #include <KIMAP/KIMAP/AppendJob> | 26 | #include <KIMAP/KIMAP/AppendJob> |
26 | #include <KIMAP/KIMAP/CreateJob> | 27 | #include <KIMAP/KIMAP/CreateJob> |
@@ -98,15 +99,33 @@ KAsync::Job<void> ImapServerProxy::login(const QString &username, const QString | |||
98 | QObject::connect(capabilitiesJob, &KIMAP::CapabilitiesJob::capabilitiesReceived, [this](const QStringList &capabilities) { | 99 | QObject::connect(capabilitiesJob, &KIMAP::CapabilitiesJob::capabilitiesReceived, [this](const QStringList &capabilities) { |
99 | mCapabilities = capabilities; | 100 | mCapabilities = capabilities; |
100 | }); | 101 | }); |
102 | auto namespaceJob = new KIMAP::NamespaceJob(mSession); | ||
103 | |||
101 | return runJob(loginJob).then(runJob(capabilitiesJob)).then<void>([this](){ | 104 | return runJob(loginJob).then(runJob(capabilitiesJob)).then<void>([this](){ |
102 | Trace() << "Supported capabilities: " << mCapabilities; | 105 | Trace() << "Supported capabilities: " << mCapabilities; |
103 | QStringList requiredExtensions = QStringList() << "UIDPLUS"; | 106 | QStringList requiredExtensions = QStringList() << "UIDPLUS" << "NAMESPACE"; |
104 | for (const auto &requiredExtension : requiredExtensions) { | 107 | for (const auto &requiredExtension : requiredExtensions) { |
105 | if (!mCapabilities.contains(requiredExtension)) { | 108 | if (!mCapabilities.contains(requiredExtension)) { |
106 | Warning() << "Server doesn't support required capability: " << requiredExtension; | 109 | Warning() << "Server doesn't support required capability: " << requiredExtension; |
107 | //TODO fail the job | 110 | //TODO fail the job |
108 | } | 111 | } |
109 | } | 112 | } |
113 | }).then(runJob(namespaceJob)).then<void>([this, namespaceJob](){ | ||
114 | for (const auto &ns :namespaceJob->personalNamespaces()) { | ||
115 | mPersonalNamespaces << ns.name; | ||
116 | mPersonalNamespaceSeparator = ns.separator; | ||
117 | } | ||
118 | for (const auto &ns :namespaceJob->sharedNamespaces()) { | ||
119 | mSharedNamespaces << ns.name; | ||
120 | mSharedNamespaceSeparator = ns.separator; | ||
121 | } | ||
122 | for (const auto &ns :namespaceJob->userNamespaces()) { | ||
123 | mUserNamespaces << ns.name; | ||
124 | mUserNamespaceSeparator = ns.separator; | ||
125 | } | ||
126 | Trace() << "Found personal namespaces: " << mPersonalNamespaces << mPersonalNamespaceSeparator; | ||
127 | Trace() << "Found shared namespaces: " << mSharedNamespaces << mSharedNamespaceSeparator; | ||
128 | Trace() << "Found user namespaces: " << mUserNamespaces << mUserNamespaceSeparator; | ||
110 | }); | 129 | }); |
111 | } | 130 | } |
112 | 131 | ||
@@ -271,16 +290,6 @@ KAsync::Job<void> ImapServerProxy::list(KIMAP::ListJob::Option option, const std | |||
271 | // listJob->setQueriedNamespaces(serverNamespaces()); | 290 | // listJob->setQueriedNamespaces(serverNamespaces()); |
272 | QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived, | 291 | QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived, |
273 | listJob, callback); | 292 | listJob, callback); |
274 | //Figure out the separator character on the first list issued. | ||
275 | if (mSeparatorCharacter.isNull()) { | ||
276 | QObject::connect(listJob, &KIMAP::ListJob::mailBoxesReceived, | ||
277 | listJob, [this](const QList<KIMAP::MailBoxDescriptor> &mailboxes,const QList<QList<QByteArray> > &flags) { | ||
278 | if (!mailboxes.isEmpty() && mSeparatorCharacter.isNull()) { | ||
279 | mSeparatorCharacter = mailboxes.first().separator; | ||
280 | } | ||
281 | } | ||
282 | ); | ||
283 | } | ||
284 | return runJob(listJob); | 293 | return runJob(listJob); |
285 | } | 294 | } |
286 | 295 | ||
@@ -295,6 +304,40 @@ KAsync::Job<void> ImapServerProxy::remove(const QString &mailbox, const QByteArr | |||
295 | return remove(mailbox, set); | 304 | return remove(mailbox, set); |
296 | } | 305 | } |
297 | 306 | ||
307 | KAsync::Job<QString> ImapServerProxy::createSubfolder(const QString &parentMailbox, const QString &folderName) | ||
308 | { | ||
309 | auto folder = QSharedPointer<QString>::create(); | ||
310 | return KAsync::start<void, KAsync::Job<void>>([this, parentMailbox, folderName, folder]() { | ||
311 | Q_ASSERT(!mPersonalNamespaceSeparator.isNull()); | ||
312 | if (parentMailbox.isEmpty()) { | ||
313 | *folder = mPersonalNamespaces.toList().first() + folderName; | ||
314 | } else { | ||
315 | *folder = parentMailbox + mPersonalNamespaceSeparator + folderName; | ||
316 | } | ||
317 | Trace() << "Creating subfolder: " << *folder; | ||
318 | return create(*folder); | ||
319 | }) | ||
320 | .then<QString>([=]() { | ||
321 | return *folder; | ||
322 | }); | ||
323 | } | ||
324 | |||
325 | KAsync::Job<QString> ImapServerProxy::renameSubfolder(const QString &oldMailbox, const QString &newName) | ||
326 | { | ||
327 | auto folder = QSharedPointer<QString>::create(); | ||
328 | return KAsync::start<void, KAsync::Job<void>>([this, oldMailbox, newName, folder]() { | ||
329 | Q_ASSERT(!mPersonalNamespaceSeparator.isNull()); | ||
330 | auto parts = oldMailbox.split(mPersonalNamespaceSeparator); | ||
331 | parts.removeLast(); | ||
332 | *folder = parts.join(mPersonalNamespaceSeparator) + mPersonalNamespaceSeparator + newName; | ||
333 | Trace() << "Renaming subfolder: " << oldMailbox << *folder; | ||
334 | return rename(oldMailbox, *folder); | ||
335 | }) | ||
336 | .then<QString>([=]() { | ||
337 | return *folder; | ||
338 | }); | ||
339 | } | ||
340 | |||
298 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const QVector<Folder> &)> callback) | 341 | KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const QVector<Folder> &)> callback) |
299 | { | 342 | { |
300 | Trace() << "Fetching folders"; | 343 | Trace() << "Fetching folders"; |
@@ -302,17 +345,23 @@ KAsync::Job<void> ImapServerProxy::fetchFolders(std::function<void(const QVector | |||
302 | QVector<Folder> list; | 345 | QVector<Folder> list; |
303 | for (const auto &mailbox : mailboxes) { | 346 | for (const auto &mailbox : mailboxes) { |
304 | Trace() << "Found mailbox: " << mailbox.name; | 347 | Trace() << "Found mailbox: " << mailbox.name; |
305 | list << Folder{mailbox.name.split(mailbox.separator)}; | 348 | list << Folder{mailbox.name.split(mailbox.separator), mailbox.name, mailbox.separator}; |
306 | } | 349 | } |
307 | callback(list); | 350 | callback(list); |
308 | }); | 351 | }); |
309 | } | 352 | } |
310 | 353 | ||
354 | QString ImapServerProxy::mailboxFromFolder(const Folder &folder) const | ||
355 | { | ||
356 | Q_ASSERT(!mPersonalNamespaceSeparator.isNull()); | ||
357 | return folder.pathParts.join(mPersonalNamespaceSeparator); | ||
358 | } | ||
359 | |||
311 | KAsync::Job<void> ImapServerProxy::fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback) | 360 | KAsync::Job<void> ImapServerProxy::fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback) |
312 | { | 361 | { |
313 | Q_ASSERT(!mSeparatorCharacter.isNull()); | 362 | Q_ASSERT(!mPersonalNamespaceSeparator.isNull()); |
314 | return select(folder.pathParts.join(mSeparatorCharacter)).then<void, KAsync::Job<void>>([this, callback, folder]() -> KAsync::Job<void> { | 363 | return select(mailboxFromFolder(folder)).then<void, KAsync::Job<void>>([this, callback, folder]() -> KAsync::Job<void> { |
315 | return fetchHeaders(folder.pathParts.join(mSeparatorCharacter)).then<void, KAsync::Job<void>, QList<qint64>>([this, callback](const QList<qint64> &uidsToFetch){ | 364 | return fetchHeaders(mailboxFromFolder(folder)).then<void, KAsync::Job<void>, QList<qint64>>([this, callback](const QList<qint64> &uidsToFetch){ |
316 | Trace() << "Uids to fetch: " << uidsToFetch; | 365 | Trace() << "Uids to fetch: " << uidsToFetch; |
317 | if (uidsToFetch.isEmpty()) { | 366 | if (uidsToFetch.isEmpty()) { |
318 | Trace() << "Nothing to fetch"; | 367 | Trace() << "Nothing to fetch"; |
diff --git a/examples/imapresource/imapserverproxy.h b/examples/imapresource/imapserverproxy.h index 05409c5..67ff000 100644 --- a/examples/imapresource/imapserverproxy.h +++ b/examples/imapresource/imapserverproxy.h | |||
@@ -54,13 +54,29 @@ struct Folder { | |||
54 | return pathParts.join('/'); | 54 | return pathParts.join('/'); |
55 | } | 55 | } |
56 | 56 | ||
57 | QString parentPath() const | ||
58 | { | ||
59 | auto parts = pathParts; | ||
60 | parts.removeLast(); | ||
61 | return parts.join(separator); | ||
62 | } | ||
63 | |||
57 | QList<QString> pathParts; | 64 | QList<QString> pathParts; |
65 | QString path; | ||
66 | QChar separator; | ||
58 | }; | 67 | }; |
59 | 68 | ||
60 | class ImapServerProxy { | 69 | class ImapServerProxy { |
61 | KIMAP::Session *mSession; | 70 | KIMAP::Session *mSession; |
62 | QChar mSeparatorCharacter; | ||
63 | QStringList mCapabilities; | 71 | QStringList mCapabilities; |
72 | |||
73 | QSet<QString> mPersonalNamespaces; | ||
74 | QChar mPersonalNamespaceSeparator; | ||
75 | QSet<QString> mSharedNamespaces; | ||
76 | QChar mSharedNamespaceSeparator; | ||
77 | QSet<QString> mUserNamespaces; | ||
78 | QChar mUserNamespaceSeparator; | ||
79 | |||
64 | public: | 80 | public: |
65 | ImapServerProxy(const QString &serverUrl, int port); | 81 | ImapServerProxy(const QString &serverUrl, int port); |
66 | 82 | ||
@@ -93,6 +109,10 @@ public: | |||
93 | KAsync::Job<QList<qint64>> fetchHeaders(const QString &mailbox); | 109 | KAsync::Job<QList<qint64>> fetchHeaders(const QString &mailbox); |
94 | KAsync::Job<void> remove(const QString &mailbox, const KIMAP::ImapSet &set); | 110 | KAsync::Job<void> remove(const QString &mailbox, const KIMAP::ImapSet &set); |
95 | KAsync::Job<void> remove(const QString &mailbox, const QByteArray &imapSet); | 111 | KAsync::Job<void> remove(const QString &mailbox, const QByteArray &imapSet); |
112 | KAsync::Job<QString> createSubfolder(const QString &parentMailbox, const QString &folderName); | ||
113 | KAsync::Job<QString> renameSubfolder(const QString &mailbox, const QString &newName); | ||
114 | |||
115 | QString mailboxFromFolder(const Folder &) const; | ||
96 | 116 | ||
97 | KAsync::Job<void> fetchFolders(std::function<void(const QVector<Folder> &)> callback); | 117 | KAsync::Job<void> fetchFolders(std::function<void(const QVector<Folder> &)> callback); |
98 | KAsync::Job<void> fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback); | 118 | KAsync::Job<void> fetchMessages(const Folder &folder, std::function<void(const QVector<Message> &)> callback); |