blob: 9177a0ae0608139c723a04f8bf8cbe3180c068b6 [file] [log] [blame]
jareda9541812013-01-09 00:08:46 -08001#include "filesystemwatcher.h"
2#include "ui_filesystemwatcher.h"
3
Jared Lindblom4d1d00a2013-01-11 01:14:23 -08004FileSystemWatcher::FileSystemWatcher(QString dirPath, QWidget *parent) :
jareda9541812013-01-09 00:08:46 -08005 QMainWindow(parent),
Jared Lindblom4d1d00a2013-01-11 01:14:23 -08006 m_ui(new Ui::FileSystemWatcher),
jareda9541812013-01-09 00:08:46 -08007 m_watcher(new QFileSystemWatcher()),
8 m_listViewModel(new QStringListModel()),
9 m_listView(new QListView()),
Jared Lindbloma9996302013-01-12 00:25:16 -080010 m_timer(new QTimer(this)),
jareda9541812013-01-09 00:08:46 -080011 m_dirPath(dirPath)
12{
13 // setup user interface
14 m_ui->setupUi(this);
15
16 // add main directory to monitor
17 m_watcher->addPath(m_dirPath);
18
19 // create the view
20 m_listView->setModel(m_listViewModel);
21 setCentralWidget(m_listView);
22
23 // set title
24 setWindowTitle("ChronoShare");
25
26 // register signals (callback functions)
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080027 connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherCallbackSlot(QString)));
28 connect(m_timer, SIGNAL(timeout()), this, SLOT(timerCallbackSlot()));
jareda9541812013-01-09 00:08:46 -080029
Jared Lindbloma9996302013-01-12 00:25:16 -080030 // bootstrap
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080031 timerCallbackSlot();
Jared Lindbloma9996302013-01-12 00:25:16 -080032
33 // start timer
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080034 m_timer->start(60000);
jareda9541812013-01-09 00:08:46 -080035}
36
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080037FileSystemWatcher::~FileSystemWatcher()
jareda9541812013-01-09 00:08:46 -080038{
39 // clean up
40 delete m_ui;
41 delete m_watcher;
42 delete m_listViewModel;
43 delete m_listView;
Jared Lindbloma9996302013-01-12 00:25:16 -080044 delete m_timer;
jareda9541812013-01-09 00:08:46 -080045}
46
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080047void FileSystemWatcher::watcherCallbackSlot(QString dirPath)
48{
49 // watcher specific steps
50 qDebug() << endl << "[WATCHER] Triggered Path: " << dirPath;
51
52 handleCallback(dirPath);
53}
54
55void FileSystemWatcher::timerCallbackSlot()
56{
57 // timer specific steps
58 qDebug() << endl << "[TIMER] Triggered Path: " << m_dirPath;
59
60 handleCallback(m_dirPath);
61}
62
63void FileSystemWatcher::handleCallback(QString dirPath)
jareda9541812013-01-09 00:08:46 -080064{
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080065 // scan directory and populate file list
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080066 QHash<QString, sFileInfo> currentState = scanDirectory(dirPath);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080067
Jared Lindblom264310d2013-01-12 01:18:58 -080068 // reconcile directory and report changes
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080069 QVector<sEventInfo> dirChanges = reconcileDirectory(currentState, dirPath);
Jared Lindbloma9996302013-01-12 00:25:16 -080070
Jared Lindblom264310d2013-01-12 01:18:58 -080071 // DEBUG: Print to Gui
Jared Lindblomf8d32ad2013-01-12 11:32:27 -080072 printToGui(dirChanges);
jareda9541812013-01-09 00:08:46 -080073}
74
Jared Lindbloma9996302013-01-12 00:25:16 -080075QHash<QString, sFileInfo> FileSystemWatcher::scanDirectory(QString dirPath)
76{
jareda9541812013-01-09 00:08:46 -080077 // list of files in directory
Jared Lindbloma9996302013-01-12 00:25:16 -080078 QHash<QString, sFileInfo> currentState;
jareda9541812013-01-09 00:08:46 -080079
80 // directory iterator (recursive)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -080081 QDirIterator dirIterator(dirPath, QDirIterator::Subdirectories |
jareda9541812013-01-09 00:08:46 -080082 QDirIterator::FollowSymlinks);
83
84 // iterate through directory recursively
85 while(dirIterator.hasNext())
86 {
87 // Get Next File/Dir
88 dirIterator.next();
89
90 // Get FileInfo
91 QFileInfo fileInfo = dirIterator.fileInfo();
92
93 // if not this directory or previous directory
94 if(fileInfo.absoluteFilePath() != ".." && fileInfo.absoluteFilePath() != ".")
95 {
Jared Lindbloma9996302013-01-12 00:25:16 -080096 QString absFilePath = fileInfo.absoluteFilePath();
97
jareda9541812013-01-09 00:08:46 -080098 // if this is a directory
99 if(fileInfo.isDir())
100 {
101 QStringList dirList = m_watcher->directories();
102
103 // if the directory is not already being watched
Jared Lindbloma9996302013-01-12 00:25:16 -0800104 if (absFilePath.startsWith(m_dirPath) && !dirList.contains(absFilePath))
jareda9541812013-01-09 00:08:46 -0800105 {
106 // add this directory to the watch list
Jared Lindbloma9996302013-01-12 00:25:16 -0800107 m_watcher->addPath(absFilePath);
jareda9541812013-01-09 00:08:46 -0800108 }
109 }
110 else
111 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800112 // construct struct
113 sFileInfo fileInfoStruct;
114 fileInfoStruct.fileInfo = fileInfo;
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800115 fileInfoStruct.hash = calcChecksum(absFilePath);
Jared Lindbloma9996302013-01-12 00:25:16 -0800116
jareda9541812013-01-09 00:08:46 -0800117 // add this file to the file list
Jared Lindbloma9996302013-01-12 00:25:16 -0800118 currentState.insert(absFilePath, fileInfoStruct);
jareda9541812013-01-09 00:08:46 -0800119 }
120 }
121 }
122
Jared Lindbloma9996302013-01-12 00:25:16 -0800123 return currentState;
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800124}
125
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800126QVector<sEventInfo> FileSystemWatcher::reconcileDirectory(QHash<QString, sFileInfo> currentState, QString dirPath)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800127{
Jared Lindbloma9996302013-01-12 00:25:16 -0800128 // list of files changed
Jared Lindblom264310d2013-01-12 01:18:58 -0800129 QVector<sEventInfo> dirChanges;
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800130
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800131 // compare result (database/stored snapshot) to fileList (current snapshot)
Jared Lindbloma9996302013-01-12 00:25:16 -0800132 QMutableHashIterator<QString, sFileInfo> i(m_storedState);
Jared Lindblom264310d2013-01-12 01:18:58 -0800133
Jared Lindbloma9996302013-01-12 00:25:16 -0800134 while(i.hasNext())
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800135 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800136 i.next();
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800137
Jared Lindbloma9996302013-01-12 00:25:16 -0800138 QString absFilePath = i.key();
139
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800140 if(!absFilePath.startsWith(dirPath))
141 {
142 continue;
143 }
144
Jared Lindbloma9996302013-01-12 00:25:16 -0800145 sFileInfo storedFileInfoStruct = i.value();
146 QFileInfo storedFileInfo = storedFileInfoStruct.fileInfo;
147 QByteArray storedHash = storedFileInfoStruct.hash;
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800148
149 // check file existence
Jared Lindbloma9996302013-01-12 00:25:16 -0800150 if(currentState.contains(absFilePath))
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800151 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800152 sFileInfo currentFileInfoStruct = currentState.value(absFilePath);
153 QFileInfo currentFileInfo = currentFileInfoStruct.fileInfo;
154 QByteArray currentHash = currentFileInfoStruct.hash;
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800155
Jared Lindbloma9996302013-01-12 00:25:16 -0800156 if((storedFileInfo != currentFileInfo) || (storedHash != currentHash))
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800157 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800158 // update stored state
159 i.setValue(currentFileInfoStruct);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800160
161 // this file has been modified
Jared Lindblom264310d2013-01-12 01:18:58 -0800162 sEventInfo eventInfo;
163 eventInfo.event = MODIFIED;
164 eventInfo.absFilePath = absFilePath;
165 dirChanges.push_back(eventInfo);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800166 }
167
Jared Lindbloma9996302013-01-12 00:25:16 -0800168 // delete this file from fileList we have processed it
169 currentState.remove(absFilePath);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800170 }
171 else
172 {
Jared Lindbloma9996302013-01-12 00:25:16 -0800173 // delete from stored state
174 i.remove();
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800175
176 // this file has been deleted
Jared Lindblom264310d2013-01-12 01:18:58 -0800177 sEventInfo eventInfo;
178 eventInfo.event = DELETED;
179 eventInfo.absFilePath = absFilePath;
180 dirChanges.push_back(eventInfo);
Jared Lindbloma9996302013-01-12 00:25:16 -0800181 }
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800182 }
183
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800184 // any files left in fileList have been added
Jared Lindbloma9996302013-01-12 00:25:16 -0800185 for(QHash<QString, sFileInfo>::iterator i = currentState.begin(); i != currentState.end(); ++i)
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800186 {
187 QString absFilePath = i.key();
Jared Lindbloma9996302013-01-12 00:25:16 -0800188 sFileInfo currentFileInfoStruct = i.value();
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800189
Jared Lindbloma9996302013-01-12 00:25:16 -0800190 m_storedState.insert(absFilePath, currentFileInfoStruct);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800191
192 // this file has been added
Jared Lindblom264310d2013-01-12 01:18:58 -0800193 sEventInfo eventInfo;
194 eventInfo.event = ADDED;
195 eventInfo.absFilePath = absFilePath;
196 dirChanges.push_back(eventInfo);
Jared Lindblom4d1d00a2013-01-11 01:14:23 -0800197 }
198
Jared Lindbloma9996302013-01-12 00:25:16 -0800199 return dirChanges;
jareda9541812013-01-09 00:08:46 -0800200}
Alexander Afanasyevff731962013-01-09 17:16:28 -0800201
Jared Lindblomf8d32ad2013-01-12 11:32:27 -0800202QByteArray FileSystemWatcher::calcChecksum(QString absFilePath)
203{
204 // initialize checksum
205 QCryptographicHash crypto(QCryptographicHash::Md5);
206
207 // open file
208 QFile file(absFilePath);
209 file.open(QFile::ReadOnly);
210
211 // calculate checksum
212 while(!file.atEnd())
213 {
214 crypto.addData(file.read(8192));
215 }
216
217 return crypto.result();
218}
219
220void FileSystemWatcher::printToGui(QVector<sEventInfo> dirChanges)
221{
222 if(!dirChanges.isEmpty())
223 {
224 QStringList dirChangesList;
225
226 for(int i = 0; i < dirChanges.size(); i++)
227 {
228 QString tempString;
229
230 eEvent event = dirChanges[i].event;
231 QString absFilePath = dirChanges[i].absFilePath;
232
233 switch(event)
234 {
235 case ADDED:
236 tempString.append("ADDED: ");
237 break;
238 case MODIFIED:
239 tempString.append("MODIFIED: ");
240 break;
241 case DELETED:
242 tempString.append("DELETED: ");
243 break;
244 }
245
246 tempString.append(absFilePath);
247
248 dirChangesList.append(tempString);
249
250 qDebug() << "\t" << tempString;
251 }
252
253 m_listViewModel->setStringList(dirChangesList);
254 }
255}
256
Alexander Afanasyevff731962013-01-09 17:16:28 -0800257#if WAF
258#include "filesystemwatcher.moc"
259#include "filesystemwatcher.cpp.moc"
260#endif