summaryrefslogtreecommitdiffstats
path: root/examples/imapresource/imapresource.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-05-22 13:10:39 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-05-22 13:10:39 +0200
commit6864e4accaafa4fa90332719bff5a85a0e92b242 (patch)
tree31c0d6df98112674e55ea5ad636c2ad780add49c /examples/imapresource/imapresource.cpp
parentde52c17a7a08e72affc4c182fb1650d18d8b3b2b (diff)
downloadsink-6864e4accaafa4fa90332719bff5a85a0e92b242.tar.gz
sink-6864e4accaafa4fa90332719bff5a85a0e92b242.zip
ImapResource prototype
Diffstat (limited to 'examples/imapresource/imapresource.cpp')
-rw-r--r--examples/imapresource/imapresource.cpp277
1 files changed, 277 insertions, 0 deletions
diff --git a/examples/imapresource/imapresource.cpp b/examples/imapresource/imapresource.cpp
new file mode 100644
index 0000000..baa88b9
--- /dev/null
+++ b/examples/imapresource/imapresource.cpp
@@ -0,0 +1,277 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "imapresource.h"
21#include "facade.h"
22#include "entitybuffer.h"
23#include "pipeline.h"
24#include "mail_generated.h"
25#include "createentity_generated.h"
26#include "modifyentity_generated.h"
27#include "deleteentity_generated.h"
28#include "domainadaptor.h"
29#include "resourceconfig.h"
30#include "commands.h"
31#include "index.h"
32#include "log.h"
33#include "domain/mail.h"
34#include "definitions.h"
35#include "facadefactory.h"
36#include "indexupdater.h"
37#include "inspection.h"
38#include <QDate>
39#include <QUuid>
40#include <QDir>
41#include <QDirIterator>
42
43#include "imapserverproxy.h"
44
45//This is the resources entity type, and not the domain type
46#define ENTITY_TYPE_MAIL "mail"
47#define ENTITY_TYPE_FOLDER "folder"
48
49#undef DEBUG_AREA
50#define DEBUG_AREA "resource.imap"
51
52
53ImapResource::ImapResource(const QByteArray &instanceIdentifier, const QSharedPointer<Sink::Pipeline> &pipeline)
54 : Sink::GenericResource(instanceIdentifier, pipeline),
55 mMailAdaptorFactory(QSharedPointer<ImapMailAdaptorFactory>::create()),
56 mFolderAdaptorFactory(QSharedPointer<ImapFolderAdaptorFactory>::create())
57{
58 auto config = ResourceConfig::getConfiguration(instanceIdentifier);
59 mServer = config.value("server").toString();
60 mPort = config.value("port").toInt();
61
62 // auto folderUpdater = new FolderUpdater(QByteArray());
63 addType(ENTITY_TYPE_MAIL, mMailAdaptorFactory,
64 QVector<Sink::Preprocessor*>() << new DefaultIndexUpdater<Sink::ApplicationDomain::Mail>);
65 addType(ENTITY_TYPE_FOLDER, mFolderAdaptorFactory,
66 QVector<Sink::Preprocessor*>() << new DefaultIndexUpdater<Sink::ApplicationDomain::Folder>);
67}
68
69QByteArray ImapResource::createFolder(const QString &folderPath, const QByteArray &icon, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction)
70{
71 auto remoteId = folderPath.toUtf8();
72 auto bufferType = ENTITY_TYPE_FOLDER;
73 Sink::ApplicationDomain::Folder folder;
74 folder.setProperty("name", folderPath.split('/').last());
75 folder.setProperty("icon", icon);
76
77 // if (!md.isRoot()) {
78 // folder.setProperty("parent", resolveRemoteId(ENTITY_TYPE_FOLDER, md.parent().path().toUtf8(), synchronizationTransaction));
79 // }
80 createOrModify(transaction, synchronizationTransaction, *mFolderAdaptorFactory, bufferType, remoteId, folder);
81 return remoteId;
82}
83
84void ImapResource::synchronizeFolders(const QStringList &folderList, Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction)
85{
86 const QByteArray bufferType = ENTITY_TYPE_FOLDER;
87 Trace() << "Found folders " << folderList;
88
89 scanForRemovals(transaction, synchronizationTransaction, bufferType,
90 [&bufferType, &transaction](const std::function<void(const QByteArray &)> &callback) {
91 //TODO Instead of iterating over all entries in the database, which can also pick up the same item multiple times,
92 //we should rather iterate over an index that contains every uid exactly once. The remoteId index would be such an index,
93 //but we currently fail to iterate over all entries in an index it seems.
94 // auto remoteIds = synchronizationTransaction.openDatabase("rid.mapping." + bufferType, std::function<void(const Sink::Storage::Error &)>(), true);
95 auto mainDatabase = Sink::Storage::mainDatabase(transaction, bufferType);
96 mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &) {
97 callback(key);
98 return true;
99 });
100 },
101 [&folderList](const QByteArray &remoteId) -> bool {
102 return folderList.contains(remoteId);
103 }
104 );
105
106 for (const auto folderPath : folderList) {
107 createFolder(folderPath, "folder", transaction, synchronizationTransaction);
108 }
109}
110
111void ImapResource::synchronizeMails(Sink::Storage::Transaction &transaction, Sink::Storage::Transaction &synchronizationTransaction, const QString &path, const QVector<Message> &messages)
112{
113 auto time = QSharedPointer<QTime>::create();
114 time->start();
115 const QByteArray bufferType = ENTITY_TYPE_MAIL;
116
117
118 Trace() << "Importing new mail.";
119
120 // Trace() << "Looking into " << listingPath;
121
122 const auto folderLocalId = resolveRemoteId(ENTITY_TYPE_FOLDER, path.toUtf8(), synchronizationTransaction);
123
124 //This is not a full listing
125 // auto property = "folder";
126 // scanForRemovals(transaction, synchronizationTransaction, bufferType,
127 // [&](const std::function<void(const QByteArray &)> &callback) {
128 // Index index(bufferType + ".index." + property, transaction);
129 // index.lookup(folderLocalId, [&](const QByteArray &sinkId) {
130 // callback(sinkId);
131 // },
132 // [&](const Index::Error &error) {
133 // Warning() << "Error in index: " << error.message << property;
134 // });
135 // },
136 // [](const QByteArray &remoteId) -> bool {
137 // return QFile(remoteId).exists();
138 // }
139 // );
140
141 mSynchronizerQueue.startTransaction();
142 int count = 0;
143 for (const auto &message : messages) {
144 count++;
145 const auto remoteId = path.toUtf8() + "/" + QByteArray::number(message.uid);
146
147 Trace() << "Found a mail " << remoteId << message.msg->subject(true)->asUnicodeString() << message.flags;
148
149 Sink::ApplicationDomain::Mail mail;
150 mail.setFolder(folderLocalId);
151 //FIXME this should come from the mime message, extracted in the pipeline
152 mail.setExtractedSubject(message.msg->subject(true)->asUnicodeString());
153
154 auto filePath = Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + remoteId;
155 QDir().mkpath(Sink::resourceStorageLocation(mResourceInstanceIdentifier) + "/" + path.toUtf8());
156 QFile file(filePath);
157 if (!file.open(QIODevice::WriteOnly)) {
158 Warning() << "Failed to open file for writing: " << file.errorString();
159 }
160 const auto content = message.msg->encodedContent();
161 file.write(content);
162 mail.setMimeMessagePath(filePath);
163 //FIXME Not sure if these are the actual flags
164 mail.setUnread(message.flags.contains("\\SEEN"));
165 mail.setImportant(message.flags.contains("\\FLAGGED"));
166
167 createOrModify(transaction, synchronizationTransaction, *mMailAdaptorFactory, bufferType, remoteId, mail);
168 }
169 mSynchronizerQueue.commit();
170 const auto elapsed = time->elapsed();
171 Log() << "Synchronized " << count << " mails in " << path << Sink::Log::TraceTime(elapsed) << " " << elapsed/qMax(count, 1) << " [ms/mail]";
172}
173
174KAsync::Job<void> ImapResource::synchronizeWithSource(Sink::Storage &mainStore, Sink::Storage &synchronizationStore)
175{
176 Log() << " Synchronizing";
177 return KAsync::start<void>([this, &mainStore, &synchronizationStore](KAsync::Future<void> future) {
178 ImapServerProxy imap(mServer, mPort);
179 QStringList folderList;
180 // QList<KAsync::Future<void>> waitCondition;
181 auto folderFuture = imap.fetchFolders([this, &imap, &mainStore, &synchronizationStore, &folderList](const QStringList &folders) {
182 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
183 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
184 synchronizeFolders(folders, transaction, syncTransaction);
185 transaction.commit();
186 syncTransaction.commit();
187 folderList << folders;
188
189 });
190 folderFuture.waitForFinished();
191 if (folderFuture.errorCode()) {
192 future.setError(1, "Folder list sync failed");
193 return;
194 }
195
196 for (const auto &folder : folderList) {
197 // auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
198 // auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadOnly);
199
200 //TODO load entity to read sync settings should we have some (if the folder is existing already)
201 //Note that this will not work if we change any of those settings in the pipeline
202 //
203 // auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_FOLDER);
204 // const auto sinkId = resolveRemoteId(ENTITY_TYPE_FOLDER, folder.toUtf8(), syncTransaction);
205 // const auto found = mainDatabase.contains(sinkId);
206 // if (found) {
207 // if (auto current = getLatest(mainDatabase, sinkId, mFolderAdaptorFactory)) {
208 //
209 // }
210 // }
211
212 // transaction.commit();
213 // syncTransaction.commit();
214
215 auto messagesFuture = imap.fetchMessages(folder, [this, &mainStore, &synchronizationStore, folder](const QVector<Message> &messages) {
216 auto transaction = mainStore.createTransaction(Sink::Storage::ReadOnly);
217 auto syncTransaction = synchronizationStore.createTransaction(Sink::Storage::ReadWrite);
218 Trace() << "Synchronizing mails" << folder;
219 synchronizeMails(transaction, syncTransaction, folder, messages);
220 transaction.commit();
221 syncTransaction.commit();
222 });
223 messagesFuture.waitForFinished();
224 if (messagesFuture.errorCode()) {
225 future.setError(1, "Folder sync failed: " + folder);
226 return;
227 }
228 }
229
230
231 // auto transaction = mainStore.createTransaction(Sink::Storage::ReadWrite);
232 // auto mainDatabase = Sink::Storage::mainDatabase(transaction, ENTITY_TYPE_FOLDER);
233 // mainDatabase.scan("", [&](const QByteArray &key, const QByteArray &data) {
234 // return true;
235 // });
236 //TODO now fetch all folders and iterate over them and synchronize each one
237
238 Log() << "Done Synchronizing";
239 future.setFinished();
240 });
241}
242
243KAsync::Job<void> ImapResource::replay(Sink::Storage &synchronizationStore, const QByteArray &type, const QByteArray &key, const QByteArray &value)
244{
245 //TODO implement
246 return KAsync::null<void>();
247}
248
249void ImapResource::removeFromDisk(const QByteArray &instanceIdentifier)
250{
251 GenericResource::removeFromDisk(instanceIdentifier);
252 Sink::Storage(Sink::storageLocation(), instanceIdentifier + ".synchronization", Sink::Storage::ReadWrite).removeFromDisk();
253}
254
255KAsync::Job<void> ImapResource::inspect(int inspectionType, const QByteArray &inspectionId, const QByteArray &domainType, const QByteArray &entityId, const QByteArray &property, const QVariant &expectedValue)
256{
257 //TODO
258 return KAsync::null<void>();
259}
260
261ImapResourceFactory::ImapResourceFactory(QObject *parent)
262 : Sink::ResourceFactory(parent)
263{
264
265}
266
267Sink::Resource *ImapResourceFactory::createResource(const QByteArray &instanceIdentifier)
268{
269 return new ImapResource(instanceIdentifier);
270}
271
272void ImapResourceFactory::registerFacades(Sink::FacadeFactory &factory)
273{
274 factory.registerFacade<Sink::ApplicationDomain::Mail, ImapResourceMailFacade>(PLUGIN_NAME);
275 factory.registerFacade<Sink::ApplicationDomain::Folder, ImapResourceFolderFacade>(PLUGIN_NAME);
276}
277