22 #include "fileindexerinterface.h"
23 #include "filewatchadaptor.h"
35 #include <QtCore/QDir>
36 #include <QtCore/QThread>
37 #include <QtDBus/QDBusConnection>
41 #include <KConfigGroup>
47 #include <Soprano/Model>
48 #include <Soprano/QueryResultIterator>
49 #include <Soprano/Node>
51 using namespace Nepomuk2::Vocabulary;
60 class IgnoringKInotify :
public KInotify
67 bool filterWatch(
const QString & path, WatchEvents & modes, WatchFlags & flags );
75 m_pathExcludeRegExpCache( rec )
79 IgnoringKInotify::~IgnoringKInotify()
83 bool IgnoringKInotify::filterWatch(
const QString & path, WatchEvents & modes, WatchFlags & flags )
87 QStringList cpts = path.split(
'/', QString::SkipEmptyParts);
93 QString file = cpts.last();
96 if( !shouldFileNameBeIndexed ) {
107 if( shouldFolderBeIndexed && shouldFileNameBeIndexed ) {
111 modes &= (~
KInotify::EventCloseWrite);
116 #endif // BUILD_KINOTIFY
121 #ifdef BUILD_KINOTIFY
129 resetStatusMessage();
141 m_metadataMoverThread =
new QThread(
this);
142 m_metadataMoverThread->start();
144 connect( m_metadataMover, SIGNAL(movedWithoutData(QString)),
145 this, SLOT(slotMovedWithoutData(QString)),
146 Qt::QueuedConnection );
148 this, SLOT(updateStatusMessage(QString)),
149 Qt::QueuedConnection );
151 this, SLOT(resetStatusMessage()),
152 Qt::QueuedConnection );
153 m_metadataMover->moveToThread(m_metadataMoverThread);
156 connect(m_fileModificationQueue, SIGNAL(urlTimeout(QString)),
157 this, SLOT(slotActiveFileQueueTimeout(QString)));
159 #ifdef BUILD_KINOTIFY
161 m_dirWatch =
new IgnoringKInotify( m_pathExcludeRegExpCache,
this );
163 connect( m_dirWatch, SIGNAL( moved( QString, QString ) ),
164 this, SLOT( slotFileMoved( QString, QString ) ) );
165 connect( m_dirWatch, SIGNAL( deleted( QString,
bool ) ),
166 this, SLOT( slotFileDeleted( QString,
bool ) ) );
167 connect( m_dirWatch, SIGNAL( created( QString,
bool ) ),
168 this, SLOT( slotFileCreated( QString,
bool ) ) );
169 connect( m_dirWatch, SIGNAL( closedWrite( QString ) ),
170 this, SLOT( slotFileClosedAfterWrite( QString ) ) );
171 connect( m_dirWatch, SIGNAL( watchUserLimitReached( QString ) ),
172 this, SLOT( slotInotifyWatchUserLimitReached( QString ) ) );
183 const QString home = QDir::homePath();
188 foreach(
const QString & folder, folders ) {
189 if( !folder.startsWith( home ) ) {
190 watchFolder( folder );
194 connectToKDirNotify();
203 addWatchesForMountedRemovableMedia();
206 this, SLOT( updateIndexedFoldersWatches() ) );
213 m_metadataMoverThread->quit();
214 m_metadataMoverThread->wait();
215 delete m_metadataMover;
220 void Nepomuk2::FileWatch::watchFolder(
const QString& path )
223 #ifdef BUILD_KINOTIFY
224 if ( m_dirWatch && !m_dirWatch->watchingPath( path ) )
225 m_dirWatch->addWatch( path,
227 KInotify::WatchFlags() );
238 return m_statusMessage;
241 void Nepomuk2::FileWatch::slotFileMoved(
const QString& urlFrom,
const QString& urlTo )
243 if( !ignorePath( urlFrom ) || !ignorePath( urlTo ) ) {
244 const KUrl from( urlFrom );
245 const KUrl to( urlTo );
246 m_metadataMover->moveFileMetadata( from, to );
251 void Nepomuk2::FileWatch::slotFilesDeleted(
const QStringList& paths )
254 foreach(
const QString& path, paths ) {
255 if( !ignorePath( path ) ) {
260 if( !urls.isEmpty() ) {
261 m_metadataMover->removeFileMetadata( urls );
266 void Nepomuk2::FileWatch::slotFileDeleted(
const QString& urlString,
bool isDir )
269 QString url = urlString;
270 if( isDir && url[ url.length() - 1 ] !=
'/') {
273 slotFilesDeleted( QStringList( url ) );
277 void Nepomuk2::FileWatch::slotFileCreated(
const QString& path,
bool isDir )
282 updateFileViaFileIndexer(path);
286 void Nepomuk2::FileWatch::slotFileClosedAfterWrite(
const QString& path )
288 QDateTime current = QDateTime::currentDateTime();
289 QDateTime fileModification = QFileInfo(path).lastModified();
290 QDateTime dirModification = QFileInfo(QFileInfo(path).absoluteDir().absolutePath()).lastModified();
297 if( fileModification.secsTo(current) <= 1000 * 60 ||
298 dirModification.secsTo(current) <= 1000 * 60) {
299 m_fileModificationQueue->enqueueUrl( path );
303 void Nepomuk2::FileWatch::slotMovedWithoutData(
const QString& path )
305 updateFileViaFileIndexer( path );
313 org::kde::nepomuk::FileIndexer fileIndexer(
"org.kde.nepomuk.services.nepomukfileindexer",
"/nepomukfileindexer", QDBusConnection::sessionBus() );
314 if ( fileIndexer.isValid() ) {
315 fileIndexer.indexFile( path );
329 org::kde::nepomuk::FileIndexer fileIndexer(
"org.kde.nepomuk.services.nepomukfileindexer",
"/nepomukfileindexer", QDBusConnection::sessionBus() );
330 if ( fileIndexer.isValid() ) {
331 fileIndexer.updateFolder( path,
false ,
false );
337 void Nepomuk2::FileWatch::connectToKDirNotify()
340 QDBusConnection::sessionBus().connect( QString(), QString(),
"org.kde.KDirNotify",
"FileMoved",
341 this, SIGNAL( slotFileMoved(
const QString&,
const QString& ) ) );
342 QDBusConnection::sessionBus().connect( QString(), QString(),
"org.kde.KDirNotify",
"FilesRemoved",
343 this, SIGNAL( slotFilesDeleted(
const QStringList& ) ) );
347 #ifdef BUILD_KINOTIFY
354 bool raiseWatchLimit() {
355 KAuth::Action limitAction(
"org.kde.nepomuk.filewatch.raiselimit");
356 limitAction.setHelperID(
"org.kde.nepomuk.filewatch");
358 KAuth::ActionReply reply = limitAction.execute();
359 if (reply.failed()) {
367 void Nepomuk2::FileWatch::slotInotifyWatchUserLimitReached(
const QString& path )
369 if ( raiseWatchLimit() ) {
370 kDebug() <<
"Successfully raised watch limit, re-adding " << path;
372 m_dirWatch->resetUserLimit();
378 syslog(LOG_USER | LOG_WARNING,
"KDE Nepomuk Filewatch service reached the folder watch limit. File changes may be ignored.");
382 m_dirWatch->deleteLater();
385 connectToKDirNotify();
391 bool Nepomuk2::FileWatch::ignorePath(
const QString& path )
395 return m_pathExcludeRegExpCache->filenameMatch( path );
399 void Nepomuk2::FileWatch::updateIndexedFoldersWatches()
401 #ifdef BUILD_KINOTIFY
404 foreach(
const QString & folder, folders ) {
405 m_dirWatch->removeWatch( folder );
406 watchFolder( folder );
413 void Nepomuk2::FileWatch::addWatchesForMountedRemovableMedia()
415 Q_FOREACH(
const RemovableMediaCache::Entry* entry, m_removableMediaCache->allMedia()) {
416 if(entry->isMounted())
417 slotDeviceMounted(entry);
426 KConfig fileIndexerConfig(
"nepomukstrigirc" );
427 const QByteArray devGroupName(
"Device-" + entry->
url().toUtf8() );
430 if(fileIndexerConfig.group(
"Devices").hasKey(entry->
url())) {
431 KConfigGroup devicesGroup = fileIndexerConfig.group(
"Devices" );
432 index = devicesGroup.readEntry(entry->
url(),
false) ? 1 : -1;
433 devicesGroup.deleteEntry( entry->
url() );
435 KConfigGroup devGroup = fileIndexerConfig.group( devGroupName );
436 devGroup.writeEntry(
"mount path", entry->
mountPath() );
438 devGroup.writeEntry(
"folders", QStringList() << QLatin1String(
"/") );
439 else if( index == -1 )
440 devGroup.writeEntry(
"exclude folders", QStringList() << QLatin1String(
"/") );
442 fileIndexerConfig.sync();
445 RemovableMediaDataMigrator* migrator =
new RemovableMediaDataMigrator( entry );
446 QThreadPool::globalInstance()->start( migrator );
450 else if( fileIndexerConfig.hasGroup( devGroupName ) ) {
453 org::kde::nepomuk::FileIndexer fileIndexer(
"org.kde.nepomuk.services.nepomukfileindexer",
"/nepomukfileindexer", QDBusConnection::sessionBus() );
454 if ( fileIndexer.isValid() ) {
455 KConfigGroup group = fileIndexerConfig.group( devGroupName );
456 const QString mountPath = group.readEntry(
"mount path", QString() );
457 if( !mountPath.isEmpty() ) {
458 const QStringList folders = group.readPathEntry(
"folders", QStringList());
459 foreach(
const QString& folder, folders ) {
460 QString path = mountPath + folder;
461 fileIndexer.indexFolder( path,
true,
false );
469 const bool indexNewlyMounted = fileIndexerConfig.group(
"RemovableMedia" ).readEntry(
"index newly mounted",
false );
470 const bool askIndividually = fileIndexerConfig.group(
"RemovableMedia" ).readEntry(
"ask user",
false );
472 if( index == 0 && indexNewlyMounted && !askIndividually ) {
473 KConfigGroup deviceGroup = fileIndexerConfig.group(
"Device-" + entry->
url() );
474 deviceGroup.writeEntry(
"folders", QStringList() << entry->
mountPath() );
480 else if( index == 0 && indexNewlyMounted && askIndividually ) {
481 kDebug() <<
"Device unknown. Asking user for action.";
488 KConfig config(
"nepomukstrigirc");
489 KConfigGroup cfg = config.group(
"RemovableMedia" );
491 if( cfg.readEntry<
bool>(
"add watches",
true ) ) {
497 if( entry->
device().isDeviceInterface( Solid::DeviceInterface::NetworkShare ) ) {
498 if( cfg.readEntry<
bool>(
"add watches network share",
false ) ) {
499 kDebug() <<
"Installing watch for network share at mount point" << path;
504 kDebug() <<
"Installing watch for removable storage at mount point" << path;
514 #ifdef BUILD_KINOTIFY
517 m_dirWatch->removeWatch( entry->
mountPath() );
523 void Nepomuk2::FileWatch::slotActiveFileQueueTimeout(
const QString& url)
526 updateFileViaFileIndexer(url);
529 void Nepomuk2::FileWatch::updateStatusMessage(
const QString &newStatus)
531 m_statusMessage = newStatus;
534 emit metadataUpdateStarted();
538 emit status(1, m_statusMessage);
541 void Nepomuk2::FileWatch::resetStatusMessage()
545 m_statusMessage = i18n(
"The File Watcher is idle." );
547 emit metadataUpdateStopped();
548 emit status( 0, m_statusMessage );
551 int main(
int argc,
char **argv ) {
552 KAboutData aboutData(
"nepomukfilewatch",
554 ki18n(
"Nepomuk File Watch"),
555 NEPOMUK_VERSION_STRING,
556 ki18n(
"Nepomuk File Watch"),
557 KAboutData::License_GPL,
558 ki18n(
"(c) 2008-2013, Sebastian Trüg"),
560 "http://nepomuk.kde.org" );
561 aboutData.addAuthor(ki18n(
"Sebastian Trüg"),ki18n(
"Developer"),
"trueg@kde.org");
562 aboutData.addAuthor(ki18n(
"Vishesh Handa"),ki18n(
"Maintainer"),
"me@vhanda.in");
564 Nepomuk2::Service2::initUI<Nepomuk2::FileWatch>( argc, argv, aboutData );
567 #include "nepomukfilewatch.moc"
Q_SCRIPTABLE bool isUpdatingMetaData() const
Returns if the watcher is doing something.
A simple wrapper around inotify which only allows to add folders recursively.
void rebuildCacheFromFilterList(const QStringList &filters)
QString mountPath() const
int main(int argc, char **argv)
File/directory created in watched directory (compare inotify's IN_CREATE)
The active file queue maintains a queue of file paths with a timestamp.
QStringList includeFolders() const
The folders to search for files to analyze.
Active config class which emits signals if the config was changed, for example if the KCM saved the c...
Solid::Device device() const
Watched file/directory was itself deleted (compare inotify's IN_DELETE_SELF)
bool folderInFolderList(const QString &path)
Returns true if the folder is in the list indexed directories and not in the list of exclude director...
New Base class for all Nepomuk services.
bool shouldFileBeIndexed(const QString &fileName) const
Check fileName for all exclude filters.
Q_SCRIPTABLE void metadataUpdateStopped()
Emitted when the watcher stops to do something.
static void updateFolderViaFileIndexer(const QString &path)
Tells the file indexer to update the folder at path or the folder containing path in case it is a fil...
NEPOMUKCOMMON_EXPORT QStringList defaultExcludeFilterList()
File opened for writing was closed (compare inotify's IN_CLOSE_WRITE)
static ResourceManager * instance()
static void updateFileViaFileIndexer(const QString &path)
Tells the file indexer to update the file (it can also be a folder but then updating will not be recu...
Q_SCRIPTABLE QString statusMessage() const
Returns a translated status message that indicates what is happening.
static FileIndexerConfig * self()
Get the first created instance of FileIndexerConfig.