#ifndef FILESYSTEMWATCHER_H #define FILESYSTEMWATCHER_H #include #ifndef Q_WS_WIN #include #include #include #include #include #include #include #ifdef Q_WS_MAC #include #include #else #include #endif #endif #ifndef CIFS_MAGIC_NUMBER #define CIFS_MAGIC_NUMBER 0xFF534D42 #endif #ifndef NFS_SUPER_MAGIC #define NFS_SUPER_MAGIC 0x6969 #endif /* * Subclassing QFileSystemWatcher in order to support Network File * System watching (NFS, CIFS) on Linux and Mac OS. */ class FileSystemWatcher: public QFileSystemWatcher { Q_OBJECT #ifndef Q_WS_WIN private: QDir watched_folder; QPointer watch_timer; QStringList filters; protected: bool isNetworkFileSystem(QString path) { QString file = path; if(!file.endsWith(QDir::separator())) file += QDir::separator(); file += "."; struct statfs buf; if(!statfs(file.toLocal8Bit().data(), &buf)) { return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC); } else { std::cerr << "Error: statfs() call failed for " << file.toLocal8Bit().data() << ". Supposing it is a local folder..." << std::endl; switch(errno) { case EACCES: std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl; break; case EFAULT: std::cerr << "Buf or path points to an invalid address" << std::endl; break; case EINTR: std::cerr << "This call was interrupted by a signal" << std::endl; break; case EIO: std::cerr << "I/O Error" << std::endl; break; case ELOOP: std::cerr << "Too many symlinks" << std::endl; break; case ENAMETOOLONG: std::cerr << "path is too long" << std::endl; break; case ENOENT: std::cerr << "The file referred by path does not exist" << std::endl; break; case ENOMEM: std::cerr << "Insufficient kernel memory" << std::endl; break; case ENOSYS: std::cerr << "The file system does not detect this call" << std::endl; break; case ENOTDIR: std::cerr << "A component of the path is not a directory" << std::endl; break; case EOVERFLOW: std::cerr << "Some values were too large to be represented in the struct" << std::endl; break; default: std::cerr << "Unknown error" << std::endl; } std::cerr << "Errno: " << errno << std::endl; return false; } } #endif public: FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) { filters << "*.torrent"; connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanFolder())); } FileSystemWatcher(QString path, QObject *parent): QFileSystemWatcher(parent) { filters << "*.torrent"; connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanFolder())); addPath(path); } ~FileSystemWatcher() { #ifndef Q_WS_WIN if(watch_timer) delete watch_timer; #endif } QStringList directories() const { #ifndef Q_WS_WIN if(watch_timer) return QStringList(watched_folder.path()); #endif return QFileSystemWatcher::directories(); } void addPath(const QString & path) { #ifndef Q_WS_WIN watched_folder = QDir(path); if(!watched_folder.exists()) return; // Check if the path points to a network file system or not if(isNetworkFileSystem(path)) { // Network mode Q_ASSERT(!watch_timer); qDebug("Network folder detected: %s", path.toLocal8Bit().data()); qDebug("Using file polling mode instead of inotify..."); // Set up the watch timer watch_timer = new QTimer(this); connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanFolder())); watch_timer->start(5000); // 5 sec } else { #endif // Normal mode qDebug("FS Watching is watching %s in normal mode", path.toLocal8Bit().data()); QFileSystemWatcher::addPath(path); scanFolder(); #ifndef Q_WS_WIN } #endif } void removePath(const QString & path) { #ifndef Q_WS_WIN if(watch_timer) { // Network mode if(QDir(path) == watched_folder) { delete watch_timer; } } else { #endif // Normal mode QFileSystemWatcher::removePath(path); #ifndef Q_WS_WIN } #endif } protected slots: // XXX: Does not detect file size changes to improve performance. void scanFolder() { qDebug("Scan folder was called"); QStringList torrents; if(watch_timer) { torrents = watched_folder.entryList(filters, QDir::Files, QDir::Unsorted); } else { torrents = QDir(QFileSystemWatcher::directories().first()).entryList(filters, QDir::Files, QDir::Unsorted); qDebug("FSWatcher: Polling manually folder %s", QFileSystemWatcher::directories().first().toLocal8Bit().data()); } if(!torrents.empty()) emit torrentsAdded(torrents); } signals: void torrentsAdded(QStringList &pathList); }; #endif // FILESYSTEMWATCHER_H