qca/plugins/qca-gnupg/ringwatch.cpp
2020-02-24 16:24:46 +01:00

186 lines
4.1 KiB
C++

/*
* Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
// since keyring files are often modified by creating a new copy and
// overwriting the original file, this messes up Qt's file watching
// capability since the original file goes away. to work around this
// problem, we'll watch the directories containing the keyring files
// instead of watching the actual files themselves.
//
// FIXME: qca 2.0.1 FileWatch has this logic already, so we can probably
// simplify this class.
#include "qca_safetimer.h"
#include "qca_support.h"
#include "ringwatch.h"
#include <QFileInfo>
using namespace QCA;
namespace gpgQCAPlugin
{
RingWatch::RingWatch(QObject *parent)
: QObject(parent)
{
}
RingWatch::~RingWatch()
{
clear();
}
void RingWatch::add(const QString &filePath)
{
QFileInfo fi(filePath);
// Try to avoid symbolic links
QString path = fi.canonicalPath();
if (path.isEmpty())
path = fi.absolutePath();
// watching this path already?
DirWatch *dirWatch = nullptr;
foreach(const DirItem &di, dirs)
{
if(di.dirWatch->dirName() == path)
{
dirWatch = di.dirWatch;
break;
}
}
// if not, make a watcher
if(!dirWatch)
{
//printf("creating dirwatch for [%s]\n", qPrintable(path));
DirItem di;
di.dirWatch = new DirWatch(path, this);
connect(di.dirWatch, &DirWatch::changed, this, &RingWatch::dirChanged);
di.changeTimer = new SafeTimer(this);
di.changeTimer->setSingleShot(true);
connect(di.changeTimer, &SafeTimer::timeout, this, &RingWatch::handleChanged);
dirWatch = di.dirWatch;
dirs += di;
}
FileItem i;
i.dirWatch = dirWatch;
i.fileName = fi.fileName();
i.exists = fi.exists();
if(i.exists)
{
i.size = fi.size();
i.lastModified = fi.lastModified();
}
files += i;
//printf("watching [%s] in [%s]\n", qPrintable(fi.fileName()), qPrintable(i.dirWatch->dirName()));
}
void RingWatch::clear()
{
files.clear();
foreach(const DirItem &di, dirs)
{
delete di.changeTimer;
delete di.dirWatch;
}
dirs.clear();
}
void RingWatch::dirChanged()
{
DirWatch *dirWatch = (DirWatch *)sender();
int at = -1;
for(int n = 0; n < dirs.count(); ++n)
{
if(dirs[n].dirWatch == dirWatch)
{
at = n;
break;
}
}
if(at == -1)
return;
// we get a ton of change notifications for the dir when
// something happens.. let's collect them and only
// report after 100ms
if(!dirs[at].changeTimer->isActive())
dirs[at].changeTimer->start(100);
}
void RingWatch::handleChanged()
{
SafeTimer *t = (SafeTimer *)sender();
int at = -1;
for(int n = 0; n < dirs.count(); ++n)
{
if(dirs[n].changeTimer == t)
{
at = n;
break;
}
}
if(at == -1)
return;
DirWatch *dirWatch = dirs[at].dirWatch;
const QString dir = dirWatch->dirName();
// see which files changed
QStringList changeList;
for(int n = 0; n < files.count(); ++n)
{
FileItem &i = files[n];
QString filePath = dir + QLatin1Char('/') + i.fileName;
QFileInfo fi(filePath);
// if the file didn't exist, and still doesn't, skip
if(!i.exists && !fi.exists())
continue;
// size/lastModified should only get checked here if
// the file existed and still exists
if(fi.exists() != i.exists || fi.size() != i.size || fi.lastModified() != i.lastModified)
{
changeList += filePath;
i.exists = fi.exists();
if(i.exists)
{
i.size = fi.size();
i.lastModified = fi.lastModified();
}
}
}
foreach(const QString &s, changeList)
emit changed(s);
}
} // end namespace gpgQCAPlugin