mirror of
https://github.com/nextcloud/desktop.git
synced 2024-12-18 20:02:17 +03:00
d1f0578bf8
removed moc include.
280 lines
7.7 KiB
C++
280 lines
7.7 KiB
C++
/*
|
|
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
// event masks
|
|
#include <stdint.h>
|
|
|
|
#include <QFileInfo>
|
|
#include <QFlags>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QMutexLocker>
|
|
#include <QStringList>
|
|
#include <QTimer>
|
|
|
|
#include "mirall/folder.h"
|
|
#include "mirall/inotify.h"
|
|
#include "mirall/folderwatcher.h"
|
|
#include "mirall/fileutils.h"
|
|
|
|
#ifdef USE_WATCHER
|
|
#include <sys/inotify.h>
|
|
#endif
|
|
|
|
static const uint32_t standard_event_mask =
|
|
#ifdef USE_WATCHER
|
|
IN_CLOSE_WRITE | IN_ATTRIB | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | IN_ONLYDIR | IN_DONT_FOLLOW;
|
|
#else
|
|
0;
|
|
#endif
|
|
/* minimum amount of seconds between two
|
|
events to consider it a new event */
|
|
#define DEFAULT_EVENT_INTERVAL_MSEC 1000
|
|
|
|
namespace Mirall {
|
|
|
|
FolderWatcher::FolderWatcher(const QString &root, QObject *parent)
|
|
: QObject(parent),
|
|
_eventsEnabled(true),
|
|
_eventInterval(DEFAULT_EVENT_INTERVAL_MSEC),
|
|
_root(root),
|
|
_processTimer(new QTimer(this)),
|
|
_lastMask(0),
|
|
_initialSyncDone(false)
|
|
{
|
|
// this is not the best place for this
|
|
addIgnore(".unison*");
|
|
addIgnore("*csync_timediff.ctmp*");
|
|
addIgnore(".*.sw?"); // vi swap files
|
|
addIgnore(".*.*.sw?");
|
|
#ifdef USE_WATCHER
|
|
_processTimer->setSingleShot(true);
|
|
QObject::connect(_processTimer, SIGNAL(timeout()), this, SLOT(slotProcessTimerTimeout()));
|
|
|
|
_inotify = new INotify(standard_event_mask);
|
|
slotAddFolderRecursive(root);
|
|
QObject::connect(_inotify, SIGNAL(notifyEvent(int, int, const QString &)),
|
|
SLOT(slotINotifyEvent(int, int, const QString &)));
|
|
#endif
|
|
// do a first synchronization to get changes while
|
|
// the application was not running
|
|
setProcessTimer();
|
|
}
|
|
|
|
FolderWatcher::~FolderWatcher()
|
|
{
|
|
|
|
}
|
|
|
|
QString FolderWatcher::root() const
|
|
{
|
|
return _root;
|
|
}
|
|
|
|
void FolderWatcher::addIgnore(const QString &pattern)
|
|
{
|
|
_ignores.append(pattern);
|
|
}
|
|
|
|
bool FolderWatcher::eventsEnabled() const
|
|
{
|
|
return _eventsEnabled;
|
|
}
|
|
|
|
void FolderWatcher::setEventsEnabled(bool enabled)
|
|
{
|
|
qDebug() << " * event notification " << (enabled ? "enabled" : "disabled");
|
|
_eventsEnabled = enabled;
|
|
if (_eventsEnabled) {
|
|
// schedule a queue cleanup for accumulated events
|
|
if ( _pendingPathes.empty() )
|
|
return;
|
|
setProcessTimer();
|
|
}
|
|
else
|
|
{
|
|
// if we are disabling events, clear any ongoing timer
|
|
if (_processTimer->isActive())
|
|
_processTimer->stop();
|
|
}
|
|
}
|
|
|
|
void FolderWatcher::clearPendingEvents()
|
|
{
|
|
if (_processTimer->isActive())
|
|
_processTimer->stop();
|
|
_pendingPathes.clear();
|
|
}
|
|
|
|
int FolderWatcher::eventInterval() const
|
|
{
|
|
return _eventInterval;
|
|
}
|
|
|
|
void FolderWatcher::setEventInterval(int seconds)
|
|
{
|
|
_eventInterval = seconds;
|
|
}
|
|
|
|
QStringList FolderWatcher::folders() const
|
|
{
|
|
#ifdef USE_WATCHER
|
|
return _inotify->directories();
|
|
#endif
|
|
}
|
|
|
|
void FolderWatcher::slotAddFolderRecursive(const QString &path)
|
|
{
|
|
int subdirs = 0;
|
|
qDebug() << "(+) Watcher:" << path;
|
|
#ifdef USE_WATCHER
|
|
|
|
_inotify->addPath(path);
|
|
QStringList watchedFolders(_inotify->directories());
|
|
// qDebug() << "currently watching " << watchedFolders;
|
|
QStringListIterator subfoldersIt(FileUtils::subFoldersList(path, FileUtils::SubFolderRecursive));
|
|
while (subfoldersIt.hasNext()) {
|
|
QString subfolder = subfoldersIt.next();
|
|
// qDebug() << " (**) subfolder: " << subfolder;
|
|
QDir folder (subfolder);
|
|
if (folder.exists() && !watchedFolders.contains(folder.path())) {
|
|
subdirs++;
|
|
// check that it does not match the ignore list
|
|
foreach (QString pattern, _ignores) {
|
|
QRegExp regexp(pattern);
|
|
regexp.setPatternSyntax(QRegExp::Wildcard);
|
|
if ( regexp.exactMatch(folder.path()) ) {
|
|
qDebug() << "* Not adding" << folder.path();
|
|
continue;
|
|
}
|
|
|
|
}
|
|
_inotify->addPath(folder.path());
|
|
}
|
|
else
|
|
qDebug() << " `-> discarded:" << folder.path();
|
|
}
|
|
if (subdirs >0)
|
|
qDebug() << " `-> and" << subdirs << "subdirectories";
|
|
#else
|
|
qDebug() << "** Watcher is not compiled in!";
|
|
#endif
|
|
}
|
|
|
|
void FolderWatcher::slotINotifyEvent(int mask, int cookie, const QString &path)
|
|
{
|
|
int lastMask = _lastMask;
|
|
QString lastPath = _lastPath;
|
|
|
|
_lastMask = mask;
|
|
_lastPath = path;
|
|
|
|
if( ! eventsEnabled() ) return;
|
|
#ifdef USE_WATCHER
|
|
qDebug() << "** Inotify Event " << mask << " on " << path;
|
|
// cancel close write events that come after create
|
|
if (lastMask == IN_CREATE && mask == IN_CLOSE_WRITE
|
|
&& lastPath == path ) {
|
|
return;
|
|
}
|
|
|
|
if (IN_IGNORED & mask) {
|
|
//qDebug() << "IGNORE event";
|
|
return;
|
|
}
|
|
|
|
if (IN_Q_OVERFLOW & mask) {
|
|
//qDebug() << "OVERFLOW";
|
|
}
|
|
|
|
if (mask & IN_CREATE) {
|
|
//qDebug() << cookie << " CREATE: " << path;
|
|
if (QFileInfo(path).isDir()) {
|
|
//setEventsEnabled(false);
|
|
slotAddFolderRecursive(path);
|
|
//setEventsEnabled(true);
|
|
}
|
|
}
|
|
else if (mask & IN_DELETE) {
|
|
//qDebug() << cookie << " DELETE: " << path;
|
|
if (_inotify->directories().contains(path) &&
|
|
QFileInfo(path).isDir());
|
|
qDebug() << "(-) Watcher:" << path;
|
|
_inotify->removePath(path);
|
|
}
|
|
else if (mask & IN_CLOSE_WRITE) {
|
|
//qDebug() << cookie << " WRITABLE CLOSED: " << path;
|
|
}
|
|
else if (mask & IN_MOVE) {
|
|
//qDebug() << cookie << " MOVE: " << path;
|
|
}
|
|
else {
|
|
//qDebug() << cookie << " OTHER " << mask << " :" << path;
|
|
}
|
|
|
|
foreach (QString pattern, _ignores) {
|
|
QRegExp regexp(pattern);
|
|
regexp.setPatternSyntax(QRegExp::Wildcard);
|
|
|
|
if (regexp.exactMatch(path)) {
|
|
qDebug() << "* Discarded " << path;
|
|
return;
|
|
}
|
|
QFileInfo fInfo(path);
|
|
if( regexp.exactMatch(fInfo.fileName())) {
|
|
qDebug() << "* Discarded " << path;
|
|
return;
|
|
}
|
|
if( fInfo.isHidden() ) {
|
|
qDebug() << "* Discarded as is hidden!";
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( !_pendingPathes.contains( path )) {
|
|
_pendingPathes[path] = 0;
|
|
}
|
|
_pendingPathes[path] = _pendingPathes[path]+mask;
|
|
#if 0
|
|
_pendingPaths[path]
|
|
_pendingPaths.append(path);
|
|
#endif
|
|
#endif
|
|
setProcessTimer();
|
|
}
|
|
|
|
void FolderWatcher::slotProcessTimerTimeout()
|
|
{
|
|
qDebug() << "* Processing of event queue for" << root();
|
|
|
|
if (!_pendingPathes.empty() || !_initialSyncDone) {
|
|
QStringList notifyPaths = _pendingPathes.keys();
|
|
_pendingPathes.clear();
|
|
//qDebug() << lastEventTime << eventTime;
|
|
qDebug() << " * Notify" << notifyPaths.size() << "changed items for" << root();
|
|
emit folderChanged(notifyPaths);
|
|
_initialSyncDone = true;
|
|
}
|
|
}
|
|
|
|
void FolderWatcher::setProcessTimer()
|
|
{
|
|
if (!_processTimer->isActive()) {
|
|
qDebug() << "* Pending events for" << root() << "will be processed after events stop for" << eventInterval() << "seconds (" << QTime::currentTime().addSecs(eventInterval()).toString("HH:mm:ss") << ")." << _pendingPathes.size() << "events until now )";
|
|
}
|
|
_processTimer->start(eventInterval());
|
|
}
|
|
|
|
}
|
|
|