2 * Copyright (C) 2011, Jamie Thompson
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 3 of the License, or (at your option) any later version.
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 GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see
16 * <http://www.gnu.org/licenses/>.
19 #include "SyncerThread.h"
21 #include "DBBackends/AllBackends.h"
22 #include "EventProcessors/Hasher.h"
23 #include "EventProcessors/Writer.h"
25 #include "Attachment.h"
26 #include "EventPreventer.h"
28 #include "EventTypes/EventFromFileList.h"
29 #include "EventTypes/iEvent.h"
30 #include "EventLogBackupManager.h"
31 #include "EventParsers/Factory.h"
32 #include "EventParsers/iEventParser.h"
39 #include <QSharedPointer>
43 typedef QPair<QFileInfo, uint> EventFileInfo;
45 QDebug operator<<(QDebug, QList<Attachment*> &);
46 QFileInfoList FindEvents(QFileInfoList);
48 SyncerThread::SyncerThread(Settings &settings) :
49 QThread(), m_Settings(settings)
55 SyncerThread::~SyncerThread()
59 m_Condition.wakeOne();
65 void SyncerThread::Sync()
74 m_Condition.wakeOne();
78 #include "NumberToNameLookup.h"
80 void SyncerThread::run()
84 EventPreventer preventEvents(CurrentSettings());
85 EventLogBackupManager backupManager(CurrentSettings());
87 if(CurrentSettings().Mode() == Settings::MODE_EXPORT)
89 qDebug() << "Exporting events";
91 // Temp - Remove directory first so it's always just this export for now
92 QDir().rmpath(CurrentSettings().Directory());
94 DBBackends::AllBackends allBackends(CurrentSettings());
95 EventProcessors::Writer eventWriter(CurrentSettings());
96 QObject::connect(&eventWriter, SIGNAL(EventProcessed(int,int)), this, SIGNAL(EventProcessed(int,int)));
97 allBackends.Process(eventWriter);
101 qDebug() << "Importing events";
103 backupManager.CreateBackup();
105 qDebug() << "Scanning filesystem";
107 // Open chosen directory and grab *all* files within (we filter at
109 QFileInfoList candidates(FindEvents(
110 QDir(CurrentSettings().Directory()).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)));
112 // We're going to be storing hashes to match up events on disk to
113 // those in the DB. Assuming 1 event per file is a good starting point.
114 // Each file can provide multiple events, so sotre the hash of the
115 // event, the file information, and the record index.
116 uint totalEvents(candidates.count());
117 QHash<QPair<QString, uint>, iHashable::Hash> hashesByPath;
118 hashesByPath.reserve(totalEvents);
119 QHash<iHashable::Hash, QPair<QString, uint> > pathsByHashes;
120 pathsByHashes.reserve(totalEvents);
122 qDebug() << "Hashing events";
124 // Work our way through the candidates...
126 foreach(QFileInfo fileInfo, candidates)
131 EventTypes::EventFromFileList fileEvents(ProcessFile(fileInfo.absoluteFilePath()));
133 foreach(EventTypes::EventFromFile eventFromFile, fileEvents)
135 const uint hash(eventFromFile.first->HashCode());
136 pathsByHashes.insert(hash, QPair<QString, iHashable::Hash>(fileInfo.absoluteFilePath(), eventFromFile.second));
137 hashesByPath.insert(QPair<QString, iHashable::Hash>(fileInfo.absoluteFilePath(), eventFromFile.second), hash);
142 catch(std::runtime_error& exception)
144 qDebug() << exception.what() << endl;
147 emit EventProcessed(idx, candidates.count());
151 qDebug() << "Determining new events";
153 // Calculate the new events by removing the hashes of events already
155 QSet<iHashable::Hash> newHashes(QSet<iHashable::Hash>::fromList( pathsByHashes.keys()));
157 //EventHasher eventHasher;
158 //ProcessDBEvents(eventHasher);
159 DBBackends::AllBackends allBackends(CurrentSettings());
160 EventProcessors::Hasher eventHasher;
161 allBackends.Process(eventHasher);
163 foreach(iHashable::Hash hash, eventHasher.m_Hashes)
164 newHashes.remove(hash);
166 qDebug() << QString("%1 new hashes").arg(newHashes.size()) << endl;
167 foreach(iHashable::Hash hash, newHashes)
168 qDebug() << hash << endl;
170 // Now an optimisation: group the new hases by the files they come
171 // from. This enables each file to only be parsed once and return
172 // all the required events from it.
173 QHash<QString, QList<iHashable::Hash> > newHashesByPath;
174 foreach(iHashable::Hash newHash, newHashes)
176 const QString ¤tPath(pathsByHashes.value(newHash).first);
177 if(newHashesByPath.contains(currentPath))
178 newHashesByPath[currentPath].append(newHash);
180 newHashesByPath[currentPath] = QList<iHashable::Hash>() << newHash;
183 qDebug() << "Scanning addressbook";
185 // Prepare the telephone-address book ID lookup.
186 NumberToNameLookup lookup;
188 qDebug() << "Importing new events";
190 // Re-parse the new events
193 foreach(QString filename, newHashesByPath.keys())
195 QList<uint> recordsToReturn;
196 foreach(iHashable::Hash newHash, newHashesByPath.value(filename))
197 recordsToReturn.append(pathsByHashes.value(newHash).second);
199 // Repeating an action that caused an exception last time
200 // shouldn't happen again, but just in case...
203 foreach(EventTypes::EventFromFile newEventFromFile, ProcessFile(filename, recordsToReturn))
205 // ...and insert it into the DB
208 allBackends.Insert(*newEventFromFile.first, lookup);
210 catch(const std::runtime_error &exception)
212 qDebug() << "Unable to insert event: " << exception.what();
215 emit EventProcessed(++idx, newHashes.count());
218 catch(const std::runtime_error &exception)
220 qDebug() << exception.what() << endl;
223 // Just to make sure the listeners are synced even if the
224 // earlier call is skipped due to errors...
225 emit EventProcessed(idx, newHashes.count());
229 // Perform any post-insert cleanup (i.e. reindexing)
230 allBackends.PostInsert();
232 // Need to find a better way of refreshing the conversations view...
233 QProcess::execute("pkill rtcom");
235 // Signal we completed successfully.
236 backupManager.UnlockCurrentBackup();
239 // May as well call this explicitly despite it being called by the
240 // destructor - it's harmless.
241 preventEvents.RestoreAccounts();
243 catch(std::runtime_error exception)
245 qDebug() << exception.what();
249 EventTypes::EventFromFileList SyncerThread::ProcessFile(const QString &path, const QList<uint> &recordsToReturn) const
251 qDebug() << path << endl;
252 QFile eventFile(path);
254 // If the file's ok, process it...
255 if (eventFile.open(QFile::ReadOnly))
257 // Identify type of file...
258 EventParsers::iEventParser * parser(EventParsers::Factory::CreateParser(CurrentSettings(), path));
260 // ...and grab the events from it (if it's a supported format)
262 return parser->ParseFile(eventFile, recordsToReturn);
264 return EventTypes::EventFromFileList();
267 throw std::runtime_error(QString("Unable to open: %1").arg(path).toStdString());
270 QFileInfoList FindEvents(QFileInfoList currentCandidate)
272 QFileInfoList foundEvents;
273 foreach(QFileInfo fileInfo, currentCandidate)
276 foundEvents.append(FindEvents(QDir(fileInfo.absoluteFilePath()).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)));
278 foundEvents.append(fileInfo);
284 QDebug operator<<(QDebug dbg, QList<Attachment*> &attachments)
286 dbg.nospace() << "Attachments" << "\n";
288 foreach(Attachment* attachment, attachments)
289 dbg.nospace() << *attachment << "\n";
291 dbg.nospace() << "\n";