#include #include #include #include namespace efsw { DirWatcherGeneric::DirWatcherGeneric( DirWatcherGeneric* parent, WatcherGeneric* ws, const std::string& directory, bool recursive, bool reportNewFiles ) : Parent( parent ), Watch( ws ), Recursive( recursive ), Deleted( false ) { resetDirectory( directory ); if ( !reportNewFiles ) { DirSnap.scan(); } else { DirectorySnapshotDiff Diff = DirSnap.scan(); if ( Diff.changed() ) { FileInfoList::iterator it; DiffIterator( FilesCreated ) { handleAction( ( *it ).Filepath, Actions::Add ); } } } } DirWatcherGeneric::~DirWatcherGeneric() { /// If the directory was deleted mark the files as deleted if ( Deleted ) { DirectorySnapshotDiff Diff = DirSnap.scan(); if ( !DirSnap.exists() ) { FileInfoList::iterator it; DiffIterator( FilesDeleted ) { handleAction( ( *it ).Filepath, Actions::Delete ); } DiffIterator( DirsDeleted ) { handleAction( ( *it ).Filepath, Actions::Delete ); } } } DirWatchMap::iterator it = Directories.begin(); for ( ; it != Directories.end(); ++it ) { if ( Deleted ) { /// If the directory was deleted, mark the flag for file deletion it->second->Deleted = true; } efSAFE_DELETE( it->second ); } } void DirWatcherGeneric::resetDirectory( std::string directory ) { std::string dir( directory ); /// Is this a recursive watch? if ( Watch->Directory != directory ) { if ( !( directory.size() && ( directory.at( 0 ) == FileSystem::getOSSlash() || directory.at( directory.size() - 1 ) == FileSystem::getOSSlash() ) ) ) { /// Get the real directory if ( NULL != Parent ) { std::string parentPath( Parent->DirSnap.DirectoryInfo.Filepath ); FileSystem::dirAddSlashAtEnd( parentPath ); FileSystem::dirAddSlashAtEnd( directory ); dir = parentPath + directory; } else { efDEBUG( "resetDirectory(): Parent is NULL. Fatal error." ); } } } DirSnap.setDirectoryInfo( dir ); } void DirWatcherGeneric::handleAction( const std::string& filename, unsigned long action, std::string oldFilename ) { Watch->Listener->handleFileAction( Watch->ID, DirSnap.DirectoryInfo.Filepath, FileSystem::fileNameFromPath( filename ), (Action)action, oldFilename ); } void DirWatcherGeneric::addChilds( bool reportNewFiles ) { if ( Recursive ) { /// Create the subdirectories watchers std::string dir; for ( FileInfoMap::iterator it = DirSnap.Files.begin(); it != DirSnap.Files.end(); it++ ) { if ( it->second.isDirectory() && it->second.isReadable() && !FileSystem::isRemoteFS( it->second.Filepath ) ) { /// Check if the directory is a symbolic link std::string curPath; std::string link( FileSystem::getLinkRealPath( it->second.Filepath, curPath ) ); dir = it->first; if ( "" != link ) { /// Avoid adding symlinks directories if it's now enabled if ( !Watch->WatcherImpl->mFileWatcher->followSymlinks() ) { continue; } /// If it's a symlink check if the realpath exists as a watcher, or /// if the path is outside the current dir if ( Watch->WatcherImpl->pathInWatches( link ) || Watch->pathInWatches( link ) || !Watch->WatcherImpl->linkAllowed( curPath, link ) ) { continue; } else { dir = link; } } else { if ( Watch->pathInWatches( dir ) || Watch->WatcherImpl->pathInWatches( dir ) ) { continue; } } if ( reportNewFiles ) { handleAction( dir, Actions::Add ); } Directories[dir] = new DirWatcherGeneric( this, Watch, dir, Recursive, reportNewFiles ); Directories[dir]->addChilds( reportNewFiles ); } } } } void DirWatcherGeneric::watch( bool reportOwnChange ) { DirectorySnapshotDiff Diff = DirSnap.scan(); if ( reportOwnChange && Diff.DirChanged && NULL != Parent ) { Watch->Listener->handleFileAction( Watch->ID, FileSystem::pathRemoveFileName( DirSnap.DirectoryInfo.Filepath ), FileSystem::fileNameFromPath( DirSnap.DirectoryInfo.Filepath ), Actions::Modified ); } if ( Diff.changed() ) { FileInfoList::iterator it; MovedList::iterator mit; /// Files DiffIterator( FilesCreated ) { handleAction( ( *it ).Filepath, Actions::Add ); } DiffIterator( FilesModified ) { handleAction( ( *it ).Filepath, Actions::Modified ); } DiffIterator( FilesDeleted ) { handleAction( ( *it ).Filepath, Actions::Delete ); } DiffMovedIterator( FilesMoved ) { handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first ); } /// Directories DiffIterator( DirsCreated ) { createDirectory( ( *it ).Filepath ); } DiffIterator( DirsModified ) { handleAction( ( *it ).Filepath, Actions::Modified ); } DiffIterator( DirsDeleted ) { handleAction( ( *it ).Filepath, Actions::Delete ); removeDirectory( ( *it ).Filepath ); } DiffMovedIterator( DirsMoved ) { handleAction( ( *mit ).second.Filepath, Actions::Moved, ( *mit ).first ); moveDirectory( ( *mit ).first, ( *mit ).second.Filepath ); } } /// Process the subdirectories looking for changes for ( DirWatchMap::iterator dit = Directories.begin(); dit != Directories.end(); ++dit ) { /// Just watch dit->second->watch(); } } void DirWatcherGeneric::watchDir( std::string& dir ) { DirWatcherGeneric* watcher = Watch->WatcherImpl->mFileWatcher->allowOutOfScopeLinks() ? findDirWatcher( dir ) : findDirWatcherFast( dir ); if ( NULL != watcher ) { watcher->watch( true ); } } DirWatcherGeneric* DirWatcherGeneric::findDirWatcherFast( std::string dir ) { // remove the common base ( dir should always start with the same base as the watcher ) efASSERT( !dir.empty() ); efASSERT( dir.size() >= DirSnap.DirectoryInfo.Filepath.size() ); efASSERT( DirSnap.DirectoryInfo.Filepath == dir.substr( 0, DirSnap.DirectoryInfo.Filepath.size() ) ); if ( dir.size() >= DirSnap.DirectoryInfo.Filepath.size() ) { dir = dir.substr( DirSnap.DirectoryInfo.Filepath.size() - 1 ); } if ( dir.size() == 1 ) { efASSERT( dir[0] == FileSystem::getOSSlash() ); return this; } size_t level = 0; std::vector dirv = String::split( dir, FileSystem::getOSSlash(), false ); DirWatcherGeneric* watcher = this; while ( level < dirv.size() ) { // search the dir level in the current watcher DirWatchMap::iterator it = watcher->Directories.find( dirv[level] ); // found? continue with the next level if ( it != watcher->Directories.end() ) { watcher = it->second; level++; } else { // couldn't found the folder level? // directory not watched return NULL; } } return watcher; } DirWatcherGeneric* DirWatcherGeneric::findDirWatcher( std::string dir ) { if ( DirSnap.DirectoryInfo.Filepath == dir ) { return this; } else { DirWatcherGeneric* watcher = NULL; for ( DirWatchMap::iterator it = Directories.begin(); it != Directories.end(); ++it ) { watcher = it->second->findDirWatcher( dir ); if ( NULL != watcher ) { return watcher; } } } return NULL; } DirWatcherGeneric* DirWatcherGeneric::createDirectory( std::string newdir ) { FileSystem::dirRemoveSlashAtEnd( newdir ); newdir = FileSystem::fileNameFromPath( newdir ); DirWatcherGeneric* dw = NULL; /// Check if the directory is a symbolic link std::string parentPath( DirSnap.DirectoryInfo.Filepath ); FileSystem::dirAddSlashAtEnd( parentPath ); std::string dir( parentPath + newdir ); FileSystem::dirAddSlashAtEnd( dir ); FileInfo fi( dir ); if ( !fi.isDirectory() || !fi.isReadable() || FileSystem::isRemoteFS( dir ) ) { return NULL; } std::string curPath; std::string link( FileSystem::getLinkRealPath( dir, curPath ) ); bool skip = false; if ( "" != link ) { /// Avoid adding symlinks directories if it's now enabled if ( !Watch->WatcherImpl->mFileWatcher->followSymlinks() ) { skip = true; } /// If it's a symlink check if the realpath exists as a watcher, or /// if the path is outside the current dir if ( Watch->WatcherImpl->pathInWatches( link ) || Watch->pathInWatches( link ) || !Watch->WatcherImpl->linkAllowed( curPath, link ) ) { skip = true; } else { dir = link; } } else { if ( Watch->pathInWatches( dir ) || Watch->WatcherImpl->pathInWatches( dir ) ) { skip = true; } } if ( !skip ) { handleAction( newdir, Actions::Add ); /// Creates the new directory watcher of the subfolder and check for new files dw = new DirWatcherGeneric( this, Watch, dir, Recursive ); dw->addChilds(); dw->watch(); /// Add it to the list of directories Directories[newdir] = dw; } return dw; } void DirWatcherGeneric::removeDirectory( std::string dir ) { FileSystem::dirRemoveSlashAtEnd( dir ); dir = FileSystem::fileNameFromPath( dir ); DirWatcherGeneric* dw = NULL; DirWatchMap::iterator dit; /// Folder deleted /// Search the folder, it should exists dit = Directories.find( dir ); if ( dit != Directories.end() ) { dw = dit->second; /// Flag it as deleted so it fire the event for every file inside deleted dw->Deleted = true; /// Delete the DirWatcherGeneric efSAFE_DELETE( dw ); /// Remove the directory from the map Directories.erase( dit->first ); } } void DirWatcherGeneric::moveDirectory( std::string oldDir, std::string newDir ) { FileSystem::dirRemoveSlashAtEnd( oldDir ); oldDir = FileSystem::fileNameFromPath( oldDir ); FileSystem::dirRemoveSlashAtEnd( newDir ); newDir = FileSystem::fileNameFromPath( newDir ); DirWatcherGeneric* dw = NULL; DirWatchMap::iterator dit; /// Directory existed? dit = Directories.find( oldDir ); if ( dit != Directories.end() ) { dw = dit->second; /// Remove the directory from the map Directories.erase( dit->first ); Directories[newDir] = dw; dw->resetDirectory( newDir ); } } bool DirWatcherGeneric::pathInWatches( std::string path ) { if ( DirSnap.DirectoryInfo.Filepath == path ) { return true; } for ( DirWatchMap::iterator it = Directories.begin(); it != Directories.end(); ++it ) { if ( it->second->pathInWatches( path ) ) { return true; } } return false; } } // namespace efsw