1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5 
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ********************************************************************
20 
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24 
25 ********************************************************************/
26 
27 #include "lockmanager.h"
28 #include "folderutils.h"
29 #include "textutils.h"
30 
31 #include <QTimer>
32 #include <QPointer>
33 #include <QMutex>
34 #include <QMutexLocker>
35 #include <QDateTime>
36 
37 const QString LockManager::LockedFileName = "___lockfile___.txt";
38 const long LockManager::FastTime =  2000;
39 const long LockManager::SlowTime = 240000;
40 
41 static LockManager TheLockManager;
42 static QHash<long, QPointer<QTimer> > TheTimers;
43 static QMultiHash<long, LockedFile *> TheLockedFiles;
44 static QMutex TheMutex;
45 
LockedFile(const QString & filename,long freq)46 LockedFile::LockedFile(const QString & filename, long freq) {
47 	file.setFileName(filename);
48 	frequency = freq;
49 }
50 
touch()51 bool LockedFile::touch() {
52 	if (file.open(QFile::WriteOnly)) {
53 		file.write("a");
54 		file.close();
55 		return true;
56 	}
57 
58 	return false;
59 }
60 
61 /////////////////////////////////////////////////
62 
LockManager()63 LockManager::LockManager() : QObject()
64 {
65 }
66 
~LockManager()67 LockManager::~LockManager()
68 {
69 	foreach (QTimer * timer, TheTimers) {
70 		if (timer) timer->stop();
71 	}
72 	TheTimers.clear();
73 }
74 
cleanup()75 void LockManager::cleanup() {
76     foreach (QTimer * timer, TheTimers) {
77 		if (timer) {
78 			timer->stop();
79 			delete timer;
80 		}
81     }
82     TheTimers.clear();
83 }
84 
touchFiles()85 void LockManager::touchFiles() {
86 	QTimer * timer = qobject_cast<QTimer *>(sender());
87 	if (timer == NULL) return;
88 
89 	QMutexLocker locker(&TheMutex);
90 	foreach (LockedFile * lockedFile, TheLockedFiles.values(timer->interval())) {
91 		lockedFile->touch();
92 	}
93 }
94 
initLockedFiles(const QString & prefix,QString & folder,QHash<QString,LockedFile * > & lockedFiles,long touchFrequency)95 void LockManager::initLockedFiles(const QString & prefix, QString & folder, QHash<QString, LockedFile *> & lockedFiles, long touchFrequency) {
96 	// first create our own unique folder and lock it
97 	QDir backupDir(FolderUtils::getUserDataStorePath(prefix));
98 	QString lockedSubfolder = TextUtils::getRandText();
99 	backupDir.mkdir(lockedSubfolder);
100 	folder = backupDir.absoluteFilePath(lockedSubfolder);
101 	if (touchFrequency > 0) {
102 		LockedFile * lockedFile = makeLockedFile(folder + "/" + LockedFileName, touchFrequency);
103 		lockedFiles.insert(lockedSubfolder, lockedFile);
104 	}
105 }
106 
makeLockedFile(const QString & path,long touchFrequency)107 LockedFile * LockManager::makeLockedFile(const QString & path, long touchFrequency) {
108 	LockedFile * lockedFile = new LockedFile(path, touchFrequency);
109 	lockedFile->touch();
110 	TheMutex.lock();
111 	TheLockedFiles.insert(touchFrequency, lockedFile);
112 	TheMutex.unlock();
113 	QTimer * timer = TheTimers.value(touchFrequency, NULL);
114 	if (timer == NULL) {
115 		timer = new QTimer();
116 		timer->setInterval(touchFrequency);
117 		timer->setSingleShot(false);
118 		QObject::connect(timer, SIGNAL(timeout()), &TheLockManager, SLOT(touchFiles()));
119 		timer->start();
120         TheTimers.insert(touchFrequency, timer);
121 	}
122 	return lockedFile;
123 }
124 
125 
releaseLockedFiles(const QString & folder,QHash<QString,LockedFile * > & lockedFiles)126 void LockManager::releaseLockedFiles(const QString & folder, QHash<QString, LockedFile *> & lockedFiles)
127 {
128 	// remove backup files; this is a clean exit
129 	releaseLockedFiles(folder, lockedFiles, true);
130 }
131 
releaseLockedFiles(const QString & folder,QHash<QString,LockedFile * > & lockedFiles,bool remove)132 void LockManager::releaseLockedFiles(const QString & folder, QHash<QString, LockedFile *> & lockedFiles, bool remove)
133 {
134 	QDir backupDir(folder);
135 	backupDir.cdUp();
136 	foreach (QString sub, lockedFiles.keys()) {
137         LockedFile * lockedFile = lockedFiles.value(sub);
138 		TheMutex.lock();
139 		TheLockedFiles.remove(lockedFile->frequency, lockedFile);
140 		TheMutex.unlock();
141         if (remove) {
142 			FolderUtils::rmdir(backupDir.absoluteFilePath(sub));
143 		}
144 		delete lockedFile;
145 	}
146 	lockedFiles.clear();
147 }
148 
checkLockedFiles(const QString & prefix,QFileInfoList & backupList,QHash<QString,LockedFile * > & lockedFiles,bool recurse,long touchFrequency)149 void LockManager::checkLockedFiles(const QString & prefix, QFileInfoList & backupList, QHash<QString, LockedFile *> & lockedFiles, bool recurse, long touchFrequency)
150 {
151 	QDir backupDir(FolderUtils::getUserDataStorePath(prefix));
152 	QFileInfoList dirList = backupDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks);
153 	foreach (QFileInfo dirInfo, dirList) {
154 		QDir dir(dirInfo.filePath());
155 		QStringList filters;
156 		//DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath()));
157 		QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks);
158 		bool gotRecurse = false;
159 		if (recurse && fileInfoList.isEmpty()) {
160 			gotRecurse = checkLockedFilesAux(dir, filters);
161 		}
162 
163 		if (fileInfoList.isEmpty() && !gotRecurse) {
164 			// could mean this backup folder is just being created by another process
165 			// could also mean it's leftover crap.
166 			// check the date and only delete if it's old
167 
168 			QDateTime lastModified = dirInfo.lastModified();
169 			if (lastModified < QDateTime::currentDateTime().addMSecs(-2000 - touchFrequency)) {
170 				FolderUtils::rmdir(dirInfo.filePath());
171 			}
172 
173 			continue;
174 		}
175 
176 		QFileInfo lockInfo(dir.absoluteFilePath(LockedFileName));
177 		if (lockInfo.exists()) {
178 			QDateTime lastModified = lockInfo.lastModified();
179 			if (lastModified >= QDateTime::currentDateTime().addMSecs(-2000 - touchFrequency)) {
180 				// somebody else owns the file
181 				continue;
182 			}
183 		}
184 
185 		// we own the file
186 		QString folder;
187 		LockedFile * lockedFile = makeLockedFile(dir.absoluteFilePath(LockedFileName), touchFrequency);
188 		lockedFiles.insert(dirInfo.fileName(), lockedFile);
189 		foreach (QFileInfo fileInfo, fileInfoList) {
190 			backupList << fileInfo;
191 		}
192 	}
193 }
194 
checkLockedFilesAux(const QDir & parent,QStringList & filters)195 bool LockManager::checkLockedFilesAux(const QDir & parent, QStringList & filters)
196 {
197 	QFileInfoList dirList = parent.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks);
198 	foreach (QFileInfo dirInfo, dirList) {
199 		QDir dir(dirInfo.filePath());
200 		//DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath()));
201 		QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks);
202 		if (!fileInfoList.isEmpty()) return true;
203 
204 		if (checkLockedFilesAux(dir, filters)) return true;
205 	}
206 
207 	return false;
208 }