diff options
Diffstat (limited to 'kio')
-rw-r--r-- | kio/Makefile.am | 5 | ||||
-rw-r--r-- | kio/kfile/kfilespeedbar.cpp | 50 | ||||
-rw-r--r-- | kio/kfile/kpropertiesdialog.cpp | 9 | ||||
-rw-r--r-- | kio/kio/kdirwatch.cpp | 28 | ||||
-rw-r--r-- | kio/kio/kdirwatch.cpp.orig | 1770 | ||||
-rw-r--r-- | kio/kio/krun.cpp | 102 | ||||
-rw-r--r-- | kio/kio/krun.h | 11 | ||||
-rw-r--r-- | kio/kio/kservice.cpp | 33 | ||||
-rw-r--r-- | kio/kio/kurlcompletion.cpp | 3 | ||||
-rw-r--r-- | kio/kpasswdserver/Makefile.am | 2 | ||||
-rw-r--r-- | kio/misc/Makefile.am | 2 | ||||
-rwxr-xr-x | kio/misc/fileshareset | 7 | ||||
-rw-r--r-- | kio/misc/kdesasl/Makefile.am | 2 | ||||
-rw-r--r-- | kio/misc/kntlm/Makefile.am | 2 | ||||
-rw-r--r-- | kio/misc/kpac/Makefile.am | 4 | ||||
-rw-r--r-- | kio/misc/kssld/Makefile.am | 2 | ||||
-rw-r--r-- | kio/misc/kwalletd/Makefile.am | 2 | ||||
-rw-r--r-- | kio/misc/kwalletd/kwalletd.cpp | 48 |
18 files changed, 2019 insertions, 63 deletions
diff --git a/kio/Makefile.am b/kio/Makefile.am index cfe25006e..d1fe7c6b2 100644 --- a/kio/Makefile.am +++ b/kio/Makefile.am @@ -20,7 +20,8 @@ AM_CPPFLAGS = -D_LARGEFILE64_SOURCE INCLUDES= -I$(srcdir)/.. $(all_includes) $(SSL_INCLUDES) -SUBDIRS = kssl kio bookmarks kfile . kpasswdserver misc pics tests kioexec httpfilter +SUBDIRS = kssl kio bookmarks kfile . pics tests kioexec httpfilter +DIST_SUBDIRS = kpasswdserver misc lib_LTLIBRARIES = libkio.la @@ -32,7 +33,7 @@ libkio_la_LIBADD = kssl/libkssl.la kio/libkiocore.la \ kio/libksycoca.la bookmarks/libkbookmarks.la kfile/libkfile.la \ ../kdeui/libkdeui.la ../kdesu/libkdesu.la \ ../kwallet/client/libkwalletclient.la \ - $(LIBZ) $(LIBFAM) $(LIBVOLMGT) $(ACL_LIBS) + $(LIBZ) $(LIBFAM) $(LIBVOLMGT) $(ACL_LIBS) $(LIB_QT) $(LIB_KDECORE) $(top_builddir)/dcop/libDCOP.la $(LIB_X11) kde_mime_DATA = magic kde_servicetypes_DATA = application.desktop kurifilterplugin.desktop \ diff --git a/kio/kfile/kfilespeedbar.cpp b/kio/kfile/kfilespeedbar.cpp index 86ee85f52..350423d35 100644 --- a/kio/kfile/kfilespeedbar.cpp +++ b/kio/kfile/kfilespeedbar.cpp @@ -20,6 +20,9 @@ #include "config-kfile.h" #include <qdir.h> +#include <qfile.h> +#include <qtextcodec.h> +#include <qtextstream.h> #include <kconfig.h> #include <kglobal.h> @@ -50,7 +53,7 @@ KFileSpeedBar::KFileSpeedBar( QWidget *parent, const char *name ) QDir(KGlobalSettings::documentPath()).exists()) { u.setPath( KGlobalSettings::documentPath() ); - insertItem( u, i18n("Documents"), false, "document" ); + insertItem( u, i18n("Documents"), false, "folder_txt" ); } u.setPath( QDir::homeDirPath() ); @@ -62,6 +65,51 @@ KFileSpeedBar::KFileSpeedBar( QWidget *parent, const char *name ) insertItem( u, i18n("Storage Media"), false, KProtocolInfo::icon( "media" ) ); + if ( QFile::exists( QDir::homeDirPath()+"/.config/user-dirs.dirs" ) ) + { + QString download, music, pictures, videos, templates, publicShares; + + QFile f( QDir::homeDirPath()+"/.config/user-dirs.dirs" ); + if (!f.open(IO_ReadOnly)) + return; + + QTextStream s( &f ); + s.setCodec( QTextCodec::codecForLocale() ); + + // read the xdg user dirs + QString line = s.readLine(); + while (!line.isNull()) + { + if (line.startsWith("XDG_DOWNLOAD_DIR=")) + download = line.remove("XDG_DOWNLOAD_DIR=").remove("\"").replace("$HOME", QDir::homeDirPath()); + else if (line.startsWith("XDG_MUSIC_DIR=")) + music = line.remove("XDG_MUSIC_DIR=").remove("\"").replace("$HOME", QDir::homeDirPath()); + else if (line.startsWith("XDG_PICTURES_DIR=")) + pictures = line.remove("XDG_PICTURES_DIR=").remove("\"").replace("$HOME", QDir::homeDirPath()); + else if (line.startsWith("XDG_VIDEOS_DIR=")) + videos = line.remove("XDG_VIDEOS_DIR=").remove("\"").replace("$HOME", QDir::homeDirPath()); + else if (line.startsWith("XDG_TEMPLATES_DIR=")) + templates = line.remove("XDG_TEMPLATES_DIR=").remove("\"").replace("$HOME", QDir::homeDirPath()); + else if (line.startsWith("XDG_PUBLICSHARES_DIR=")) + publicShares = line.remove("XDG_PUBLICSHARES_DIR=").remove("\"").replace("$HOME", QDir::homeDirPath()); + + line = s.readLine(); + } + // now add in the speedbar + if (!download.isEmpty()) + insertItem( download, i18n( "Download" ), false, "folder_html" ); + if (!music.isEmpty()) + insertItem( music, i18n( "Music" ), false, "folder_sound" ); + if (!pictures.isEmpty()) + insertItem( pictures, i18n( "Pictures" ), false, "folder_image" ); + if (!videos.isEmpty()) + insertItem( videos, i18n( "Videos" ), false, "folder_video" ); + if (!templates.isEmpty()) + insertItem( templates, i18n( "Templates" ), false, "folder_video" ); + if (!publicShares.isEmpty()) + insertItem( publicShares, i18n( "Public" ), false, "folder_video" ); + } + u = "remote:/"; if ( KProtocolInfo::isKnownProtocol( u ) ) insertItem( u, i18n("Network Folders"), false, diff --git a/kio/kfile/kpropertiesdialog.cpp b/kio/kfile/kpropertiesdialog.cpp index fd08d906d..f02e80eaf 100644 --- a/kio/kfile/kpropertiesdialog.cpp +++ b/kio/kfile/kpropertiesdialog.cpp @@ -1881,7 +1881,14 @@ static bool fileSystemSupportsACL( const QCString& pathCString ) fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS ); #else fileSystemSupportsACLs = - getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA; + getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 +#ifdef ENODATA + || (errno == ENODATA) +#endif +#ifdef ENOATTR + || (errno == ENOATTR) +#endif + ; #endif return fileSystemSupportsACLs; } diff --git a/kio/kio/kdirwatch.cpp b/kio/kio/kdirwatch.cpp index b80a23473..aaa807ed6 100644 --- a/kio/kio/kdirwatch.cpp +++ b/kio/kio/kdirwatch.cpp @@ -64,36 +64,17 @@ // debug #include <sys/ioctl.h> -#ifdef HAVE_SYS_INOTIFY -#include <sys/inotify.h> -#include <fcntl.h> -#elif HAVE_INOTIFY +#ifdef HAVE_INOTIFY #include <unistd.h> #include <fcntl.h> #include <sys/syscall.h> +#include <linux/types.h> // Linux kernel headers are documented to not compile #define _S390_BITOPS_H -#include <linux/inotify.h> - -static inline int inotify_init (void) -{ - return syscall (__NR_inotify_init); -} - -static inline int inotify_add_watch (int fd, const char *name, __u32 mask) -{ - return syscall (__NR_inotify_add_watch, fd, name, mask); -} - -static inline int inotify_rm_watch (int fd, __u32 wd) -{ - return syscall (__NR_inotify_rm_watch, fd, wd); -} -#endif +#include <sys/inotify.h> -#ifdef HAVE_INOTIFY #ifndef IN_ONLYDIR -#define IN_ONLYDIR 0x01000000 +#define IN_ONLYDIR 0x01000000 #endif #ifndef IN_DONT_FOLLOW @@ -103,6 +84,7 @@ static inline int inotify_rm_watch (int fd, __u32 wd) #ifndef IN_MOVE_SELF #define IN_MOVE_SELF 0x00000800 #endif + #endif #include <sys/utsname.h> diff --git a/kio/kio/kdirwatch.cpp.orig b/kio/kio/kdirwatch.cpp.orig new file mode 100644 index 000000000..b80a23473 --- /dev/null +++ b/kio/kio/kdirwatch.cpp.orig @@ -0,0 +1,1770 @@ +// -*- c-basic-offset: 2 -*- +/* This file is part of the KDE libraries + Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +// CHANGES: +// Oct 4, 2005 - Inotify support (Dirk Mueller) +// Februar 2002 - Add file watching and remote mount check for STAT +// Mar 30, 2001 - Native support for Linux dir change notification. +// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de) +// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven) +// May 23. 1998 - Removed static pointer - you can have more instances. +// It was Needed for KRegistry. KDirWatch now emits signals and doesn't +// call (or need) KFM. No more URL's - just plain paths. (sven) +// Mar 29. 1998 - added docs, stop/restart for particular Dirs and +// deep copies for list of dirs. (sven) +// Mar 28. 1998 - Created. (sven) + + +#include <config.h> +#include <errno.h> + +#ifdef HAVE_DNOTIFY +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#endif + + +#include <sys/stat.h> +#include <assert.h> +#include <qdir.h> +#include <qfile.h> +#include <qintdict.h> +#include <qptrlist.h> +#include <qsocketnotifier.h> +#include <qstringlist.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kstaticdeleter.h> +#include <kde_file.h> + +// debug +#include <sys/ioctl.h> + +#ifdef HAVE_SYS_INOTIFY +#include <sys/inotify.h> +#include <fcntl.h> +#elif HAVE_INOTIFY +#include <unistd.h> +#include <fcntl.h> +#include <sys/syscall.h> +// Linux kernel headers are documented to not compile +#define _S390_BITOPS_H +#include <linux/inotify.h> + +static inline int inotify_init (void) +{ + return syscall (__NR_inotify_init); +} + +static inline int inotify_add_watch (int fd, const char *name, __u32 mask) +{ + return syscall (__NR_inotify_add_watch, fd, name, mask); +} + +static inline int inotify_rm_watch (int fd, __u32 wd) +{ + return syscall (__NR_inotify_rm_watch, fd, wd); +} +#endif + +#ifdef HAVE_INOTIFY +#ifndef IN_ONLYDIR +#define IN_ONLYDIR 0x01000000 +#endif + +#ifndef IN_DONT_FOLLOW +#define IN_DONT_FOLLOW 0x02000000 +#endif + +#ifndef IN_MOVE_SELF +#define IN_MOVE_SELF 0x00000800 +#endif +#endif + +#include <sys/utsname.h> + +#include "kdirwatch.h" +#include "kdirwatch_p.h" +#include "global.h" // KIO::probably_slow_mounted + +#define NO_NOTIFY (time_t) 0 + +static KDirWatchPrivate* dwp_self = 0; + +#ifdef HAVE_DNOTIFY + +static int dnotify_signal = 0; + +/* DNOTIFY signal handler + * + * As this is called asynchronously, only a flag is set and + * a rescan is requested. + * This is done by writing into a pipe to trigger a QSocketNotifier + * watching on this pipe: a timer is started and after a timeout, + * the rescan is done. + */ +void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *) +{ + if (!dwp_self) return; + + // write might change errno, we have to save it and restore it + // (Richard Stevens, Advanced programming in the Unix Environment) + int saved_errno = errno; + + Entry* e = dwp_self->fd_Entry.find(si->si_fd); + +// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path " +// << QString(e ? e->path:"unknown") << endl; + + if(e && e->dn_fd == si->si_fd) + e->dirty = true; + + char c = 0; + write(dwp_self->mPipe[1], &c, 1); + errno = saved_errno; +} + +static struct sigaction old_sigio_act; +/* DNOTIFY SIGIO signal handler + * + * When the kernel queue for the dnotify_signal overflows, a SIGIO is send. + */ +void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p) +{ + if (dwp_self) + { + // write might change errno, we have to save it and restore it + // (Richard Stevens, Advanced programming in the Unix Environment) + int saved_errno = errno; + + dwp_self->rescan_all = true; + char c = 0; + write(dwp_self->mPipe[1], &c, 1); + + errno = saved_errno; + } + + // Call previous signal handler + if (old_sigio_act.sa_flags & SA_SIGINFO) + { + if (old_sigio_act.sa_sigaction) + (*old_sigio_act.sa_sigaction)(sig, si, p); + } + else + { + if ((old_sigio_act.sa_handler != SIG_DFL) && + (old_sigio_act.sa_handler != SIG_IGN)) + (*old_sigio_act.sa_handler)(sig); + } +} +#endif + + +// +// Class KDirWatchPrivate (singleton) +// + +/* All entries (files/directories) to be watched in the + * application (coming from multiple KDirWatch instances) + * are registered in a single KDirWatchPrivate instance. + * + * At the moment, the following methods for file watching + * are supported: + * - Polling: All files to be watched are polled regularly + * using stat (more precise: QFileInfo.lastModified()). + * The polling frequency is determined from global kconfig + * settings, defaulting to 500 ms for local directories + * and 5000 ms for remote mounts + * - FAM (File Alternation Monitor): first used on IRIX, SGI + * has ported this method to LINUX. It uses a kernel part + * (IMON, sending change events to /dev/imon) and a user + * level damon (fam), to which applications connect for + * notification of file changes. For NFS, the fam damon + * on the NFS server machine is used; if IMON is not built + * into the kernel, fam uses polling for local files. + * - DNOTIFY: In late LINUX 2.3.x, directory notification was + * introduced. By opening a directory, you can request for + * UNIX signals to be sent to the process when a directory + * is changed. + * - INOTIFY: In LINUX 2.6.13, inode change notification was + * introduced. You're now able to watch arbitrary inode's + * for changes, and even get notification when they're + * unmounted. + */ + +KDirWatchPrivate::KDirWatchPrivate() + : rescan_timer(0, "KDirWatchPrivate::rescan_timer") +{ + timer = new QTimer(this, "KDirWatchPrivate::timer"); + connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan())); + freq = 3600000; // 1 hour as upper bound + statEntries = 0; + delayRemove = false; + m_ref = 0; + + KConfigGroup config(KGlobal::config(), QCString("DirWatch")); + m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000); + m_PollInterval = config.readNumEntry("PollInterval", 500); + + QString available("Stat"); + + // used for FAM and DNOTIFY + rescan_all = false; + connect(&rescan_timer, SIGNAL(timeout()), this, SLOT(slotRescan())); + +#ifdef HAVE_FAM + // It's possible that FAM server can't be started + if (FAMOpen(&fc) ==0) { + available += ", FAM"; + use_fam=true; + sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc), + QSocketNotifier::Read, this); + connect( sn, SIGNAL(activated(int)), + this, SLOT(famEventReceived()) ); + } + else { + kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl; + use_fam=false; + } +#endif + +#ifdef HAVE_INOTIFY + supports_inotify = true; + + m_inotify_fd = inotify_init(); + + if ( m_inotify_fd <= 0 ) { + kdDebug(7001) << "Can't use Inotify, kernel doesn't support it" << endl; + supports_inotify = false; + } + + { + struct utsname uts; + int major, minor, patch; + if (uname(&uts) < 0) + supports_inotify = false; // *shrug* + else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) + supports_inotify = false; // *shrug* + else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14 + kdDebug(7001) << "Can't use INotify, Linux kernel too old" << endl; + supports_inotify = false; + } + } + + if ( supports_inotify ) { + available += ", Inotify"; + fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC); + + mSn = new QSocketNotifier( m_inotify_fd, QSocketNotifier::Read, this ); + connect( mSn, SIGNAL(activated( int )), this, SLOT( slotActivated() ) ); + } +#endif + +#ifdef HAVE_DNOTIFY + + // if we have inotify, disable dnotify. +#ifdef HAVE_INOTIFY + supports_dnotify = !supports_inotify; +#else + // otherwise, not guilty until proven guilty. + supports_dnotify = true; +#endif + + struct utsname uts; + int major, minor, patch; + if (uname(&uts) < 0) + supports_dnotify = false; // *shrug* + else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3) + supports_dnotify = false; // *shrug* + else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19 + kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl; + supports_dnotify = false; + } + + if( supports_dnotify ) { + available += ", DNotify"; + + pipe(mPipe); + fcntl(mPipe[0], F_SETFD, FD_CLOEXEC); + fcntl(mPipe[1], F_SETFD, FD_CLOEXEC); + fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL)); + fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL)); + mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this); + connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated())); + // Install the signal handler only once + if ( dnotify_signal == 0 ) + { + dnotify_signal = SIGRTMIN + 8; + + struct sigaction act; + act.sa_sigaction = KDirWatchPrivate::dnotify_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif + sigaction(dnotify_signal, &act, NULL); + + act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler; + sigaction(SIGIO, &act, &old_sigio_act); + } + } + else + { + mPipe[0] = -1; + mPipe[1] = -1; + } +#endif + + kdDebug(7001) << "Available methods: " << available << endl; +} + +/* This is called on app exit (KStaticDeleter) */ +KDirWatchPrivate::~KDirWatchPrivate() +{ + timer->stop(); + + /* remove all entries being watched */ + removeEntries(0); + +#ifdef HAVE_FAM + if (use_fam) { + FAMClose(&fc); + kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl; + } +#endif +#ifdef HAVE_INOTIFY + if ( supports_inotify ) + ::close( m_inotify_fd ); +#endif +#ifdef HAVE_DNOTIFY + close(mPipe[0]); + close(mPipe[1]); +#endif +} + +#include <stdlib.h> + +void KDirWatchPrivate::slotActivated() +{ +#ifdef HAVE_DNOTIFY + if ( supports_dnotify ) + { + char dummy_buf[4096]; + read(mPipe[0], &dummy_buf, 4096); + + if (!rescan_timer.isActive()) + rescan_timer.start(m_PollInterval, true /* singleshot */); + + return; + } +#endif + +#ifdef HAVE_INOTIFY + if ( !supports_inotify ) + return; + + int pending = -1; + int offset = 0; + char buf[4096]; + assert( m_inotify_fd > -1 ); + ioctl( m_inotify_fd, FIONREAD, &pending ); + + while ( pending > 0 ) { + + if ( pending > (int)sizeof( buf ) ) + pending = sizeof( buf ); + + pending = read( m_inotify_fd, buf, pending); + + while ( pending > 0 ) { + struct inotify_event *event = (struct inotify_event *) &buf[offset]; + pending -= sizeof( struct inotify_event ) + event->len; + offset += sizeof( struct inotify_event ) + event->len; + + QString path; + if ( event->len ) + path = QFile::decodeName( QCString( event->name, event->len ) ); + + if ( path.length() && isNoisyFile( path.latin1() ) ) + continue; + + kdDebug(7001) << "ev wd: " << event->wd << " mask " << event->mask << " path: " << path << endl; + + // now we're in deep trouble of finding the + // associated entries + // for now, we suck and iterate + for ( EntryMap::Iterator it = m_mapEntries.begin(); + it != m_mapEntries.end(); ++it ) { + Entry* e = &( *it ); + if ( e->wd == event->wd ) { + e->dirty = true; + + if ( 1 || e->isDir) { + if( event->mask & IN_DELETE_SELF) { + kdDebug(7001) << "-->got deleteself signal for " << e->path << endl; + e->m_status = NonExistent; + if (e->isDir) + addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true); + else + addEntry(0, QFileInfo(e->path).dirPath(true), e, true); + } + if ( event->mask & IN_IGNORED ) { + e->wd = 0; + } + if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) { + Entry *sub_entry = e->m_entries.first(); + for(;sub_entry; sub_entry = e->m_entries.next()) + if (sub_entry->path == e->path + "/" + path) break; + + if (sub_entry /*&& sub_entry->isDir*/) { + removeEntry(0,e->path, sub_entry); + KDE_struct_stat stat_buf; + QCString tpath = QFile::encodeName(path); + KDE_stat(tpath, &stat_buf); + + //sub_entry->isDir = S_ISDIR(stat_buf.st_mode); + //sub_entry->m_ctime = stat_buf.st_ctime; + //sub_entry->m_status = Normal; + //sub_entry->m_nlink = stat_buf.st_nlink; + + if(!useINotify(sub_entry)) + useStat(sub_entry); + sub_entry->dirty = true; + } + } + } + + if (!rescan_timer.isActive()) + rescan_timer.start(m_PollInterval, true /* singleshot */); + + break; // there really should be only one matching wd + } + } + + } + } +#endif +} + +/* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned. + * We first need to mark all yet nonexistent, but possible created + * entries as dirty... + */ +void KDirWatchPrivate::Entry::propagate_dirty() +{ + for (QPtrListIterator<Entry> sub_entry (m_entries); + sub_entry.current(); ++sub_entry) + { + if (!sub_entry.current()->dirty) + { + sub_entry.current()->dirty = true; + sub_entry.current()->propagate_dirty(); + } + } +} + + +/* A KDirWatch instance is interested in getting events for + * this file/Dir entry. + */ +void KDirWatchPrivate::Entry::addClient(KDirWatch* instance) +{ + Client* client = m_clients.first(); + for(;client; client = m_clients.next()) + if (client->instance == instance) break; + + if (client) { + client->count++; + return; + } + + client = new Client; + client->instance = instance; + client->count = 1; + client->watchingStopped = instance->isStopped(); + client->pending = NoChange; + + m_clients.append(client); +} + +void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance) +{ + Client* client = m_clients.first(); + for(;client; client = m_clients.next()) + if (client->instance == instance) break; + + if (client) { + client->count--; + if (client->count == 0) { + m_clients.removeRef(client); + delete client; + } + } +} + +/* get number of clients */ +int KDirWatchPrivate::Entry::clients() +{ + int clients = 0; + Client* client = m_clients.first(); + for(;client; client = m_clients.next()) + clients += client->count; + + return clients; +} + + +KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path) +{ +// we only support absolute paths + if (QDir::isRelativePath(_path)) { + return 0; + } + + QString path = _path; + + if ( path.length() > 1 && path.right(1) == "/" ) + path.truncate( path.length() - 1 ); + + EntryMap::Iterator it = m_mapEntries.find( path ); + if ( it == m_mapEntries.end() ) + return 0; + else + return &(*it); +} + +// set polling frequency for a entry and adjust global freq if needed +void KDirWatchPrivate::useFreq(Entry* e, int newFreq) +{ + e->freq = newFreq; + + // a reasonable frequency for the global polling timer + if (e->freq < freq) { + freq = e->freq; + if (timer->isActive()) timer->changeInterval(freq); + kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl; + } +} + + +#ifdef HAVE_FAM +// setup FAM notification, returns false if not possible +bool KDirWatchPrivate::useFAM(Entry* e) +{ + if (!use_fam) return false; + + // handle FAM events to avoid deadlock + // (FAM sends back all files in a directory when monitoring) + famEventReceived(); + + e->m_mode = FAMMode; + e->dirty = false; + + if (e->isDir) { + if (e->m_status == NonExistent) { + // If the directory does not exist we watch the parent directory + addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true); + } + else { + int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path), + &(e->fr), e); + if (res<0) { + e->m_mode = UnknownMode; + use_fam=false; + return false; + } + kdDebug(7001) << " Setup FAM (Req " + << FAMREQUEST_GETREQNUM(&(e->fr)) + << ") for " << e->path << endl; + } + } + else { + if (e->m_status == NonExistent) { + // If the file does not exist we watch the directory + addEntry(0, QFileInfo(e->path).dirPath(true), e, true); + } + else { + int res = FAMMonitorFile(&fc, QFile::encodeName(e->path), + &(e->fr), e); + if (res<0) { + e->m_mode = UnknownMode; + use_fam=false; + return false; + } + + kdDebug(7001) << " Setup FAM (Req " + << FAMREQUEST_GETREQNUM(&(e->fr)) + << ") for " << e->path << endl; + } + } + + // handle FAM events to avoid deadlock + // (FAM sends back all files in a directory when monitoring) + famEventReceived(); + + return true; +} +#endif + + +#ifdef HAVE_DNOTIFY +// setup DNotify notification, returns false if not possible +bool KDirWatchPrivate::useDNotify(Entry* e) +{ + e->dn_fd = 0; + e->dirty = false; + if (!supports_dnotify) return false; + + e->m_mode = DNotifyMode; + + if (e->isDir) { + if (e->m_status == Normal) { + int fd = KDE_open(QFile::encodeName(e->path).data(), O_RDONLY); + // Migrate fd to somewhere above 128. Some libraries have + // constructs like: + // fd = socket(...) + // if (fd > ARBITRARY_LIMIT) + // return error; + // + // Since programs might end up using a lot of KDirWatch objects + // for a rather long time the above braindamage could get + // triggered. + // + // By moving the kdirwatch fd's to > 128, calls like socket() will keep + // returning fd's < ARBITRARY_LIMIT for a bit longer. + int fd2 = fcntl(fd, F_DUPFD, 128); + if (fd2 >= 0) + { + close(fd); + fd = fd2; + } + if (fd<0) { + e->m_mode = UnknownMode; + return false; + } + + int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT; + // if dependant is a file watch, we check for MODIFY & ATTRIB too + for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) + if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; } + + if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 || + fcntl(fd, F_NOTIFY, mask) < 0) { + + kdDebug(7001) << "Not using Linux Directory Notifications." + << endl; + supports_dnotify = false; + ::close(fd); + e->m_mode = UnknownMode; + return false; + } + + fd_Entry.replace(fd, e); + e->dn_fd = fd; + + kdDebug(7001) << " Setup DNotify (fd " << fd + << ") for " << e->path << endl; + } + else { // NotExisting + addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true); + } + } + else { // File + // we always watch the directory (DNOTIFY can't watch files alone) + // this notifies us about changes of files therein + addEntry(0, QFileInfo(e->path).dirPath(true), e, true); + } + + return true; +} +#endif + +#ifdef HAVE_INOTIFY +// setup INotify notification, returns false if not possible +bool KDirWatchPrivate::useINotify( Entry* e ) +{ + e->wd = 0; + e->dirty = false; + if (!supports_inotify) return false; + + e->m_mode = INotifyMode; + + int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW; + if(!e->isDir) + mask |= IN_MODIFY|IN_ATTRIB; + else + mask |= IN_ONLYDIR; + + // if dependant is a file watch, we check for MODIFY & ATTRIB too + for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) { + if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; } + } + + if ( ( e->wd = inotify_add_watch( m_inotify_fd, + QFile::encodeName( e->path ), mask) ) > 0 ) + return true; + + if ( e->m_status == NonExistent ) { + if (e->isDir) + addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true); + else + addEntry(0, QFileInfo(e->path).dirPath(true), e, true); + return true; + } + + return false; +} +#endif + +bool KDirWatchPrivate::useStat(Entry* e) +{ + if (KIO::probably_slow_mounted(e->path)) + useFreq(e, m_nfsPollInterval); + else + useFreq(e, m_PollInterval); + + if (e->m_mode != StatMode) { + e->m_mode = StatMode; + statEntries++; + + if ( statEntries == 1 ) { + // if this was first STAT entry (=timer was stopped) + timer->start(freq); // then start the timer + kdDebug(7001) << " Started Polling Timer, freq " << freq << endl; + } + } + + kdDebug(7001) << " Setup Stat (freq " << e->freq + << ") for " << e->path << endl; + + return true; +} + + +/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>, + * providing in <isDir> the type of the entry to be watched. + * Sometimes, entries are dependant on each other: if <sub_entry> !=0, + * this entry needs another entry to watch himself (when notExistent). + */ +void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path, + Entry* sub_entry, bool isDir) +{ + QString path = _path; + if (path.startsWith("/dev/") || (path == "/dev")) + return; // Don't even go there. + + if ( path.length() > 1 && path.right(1) == "/" ) + path.truncate( path.length() - 1 ); + + EntryMap::Iterator it = m_mapEntries.find( path ); + if ( it != m_mapEntries.end() ) + { + if (sub_entry) { + (*it).m_entries.append(sub_entry); + kdDebug(7001) << "Added already watched Entry " << path + << " (for " << sub_entry->path << ")" << endl; + +#ifdef HAVE_DNOTIFY + { + Entry* e = &(*it); + if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) { + int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT; + // if dependant is a file watch, we check for MODIFY & ATTRIB too + for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) + if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; } + if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen + ::close(e->dn_fd); + e->m_mode = UnknownMode; + fd_Entry.remove(e->dn_fd); + e->dn_fd = 0; + useStat( e ); + } + } + } +#endif + +#ifdef HAVE_INOTIFY + { + Entry* e = &(*it); + if( (e->m_mode == INotifyMode) && (e->wd > 0) ) { + int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW; + if(!e->isDir) + mask |= IN_MODIFY|IN_ATTRIB; + else + mask |= IN_ONLYDIR; + + inotify_rm_watch (m_inotify_fd, e->wd); + e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ), mask); + } + } +#endif + + } + else { + (*it).addClient(instance); + kdDebug(7001) << "Added already watched Entry " << path + << " (now " << (*it).clients() << " clients)" + << QString(" [%1]").arg(instance->name()) << endl; + } + return; + } + + // we have a new path to watch + + KDE_struct_stat stat_buf; + QCString tpath = QFile::encodeName(path); + bool exists = (KDE_stat(tpath, &stat_buf) == 0); + + Entry newEntry; + m_mapEntries.insert( path, newEntry ); + // the insert does a copy, so we have to use <e> now + Entry* e = &(m_mapEntries[path]); + + if (exists) { + e->isDir = S_ISDIR(stat_buf.st_mode); + + if (e->isDir && !isDir) + kdWarning() << "KDirWatch: " << path << " is a directory. Use addDir!" << endl; + else if (!e->isDir && isDir) + kdWarning() << "KDirWatch: " << path << " is a file. Use addFile!" << endl; + + e->m_ctime = stat_buf.st_ctime; + e->m_status = Normal; + e->m_nlink = stat_buf.st_nlink; + } + else { + e->isDir = isDir; + e->m_ctime = invalid_ctime; + e->m_status = NonExistent; + e->m_nlink = 0; + } + + e->path = path; + if (sub_entry) + e->m_entries.append(sub_entry); + else + e->addClient(instance); + + kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path + << (e->m_status == NonExistent ? " NotExisting" : "") + << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString("")) + << (instance ? QString(" [%1]").arg(instance->name()) : QString("")) + << endl; + + + // now setup the notification method + e->m_mode = UnknownMode; + e->msecLeft = 0; + + if ( isNoisyFile( tpath ) ) + return; + +#ifdef HAVE_FAM + if (useFAM(e)) return; +#endif + +#ifdef HAVE_INOTIFY + if (useINotify(e)) return; +#endif + +#ifdef HAVE_DNOTIFY + if (useDNotify(e)) return; +#endif + + useStat(e); +} + + +void KDirWatchPrivate::removeEntry( KDirWatch* instance, + const QString& _path, Entry* sub_entry ) +{ + kdDebug(7001) << "KDirWatchPrivate::removeEntry for '" << _path << "' sub_entry: " << sub_entry << endl; + Entry* e = entry(_path); + if (!e) { + kdDebug(7001) << "KDirWatchPrivate::removeEntry can't handle '" << _path << "'" << endl; + return; + } + + if (sub_entry) + e->m_entries.removeRef(sub_entry); + else + e->removeClient(instance); + + if (e->m_clients.count() || e->m_entries.count()) { + kdDebug(7001) << "removeEntry: unwatched " << e->path << " " << _path << endl; + return; + } + + if (delayRemove) { + // removeList is allowed to contain any entry at most once + if (removeList.findRef(e)==-1) + removeList.append(e); + // now e->isValid() is false + return; + } + +#ifdef HAVE_FAM + if (e->m_mode == FAMMode) { + if ( e->m_status == Normal) { + FAMCancelMonitor(&fc, &(e->fr) ); + kdDebug(7001) << "Cancelled FAM (Req " + << FAMREQUEST_GETREQNUM(&(e->fr)) + << ") for " << e->path << endl; + } + else { + if (e->isDir) + removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); + else + removeEntry(0, QFileInfo(e->path).dirPath(true), e); + } + } +#endif + +#ifdef HAVE_INOTIFY + kdDebug(7001) << "inotify remove " << ( e->m_mode == INotifyMode ) << " " << ( e->m_status == Normal ) << endl; + if (e->m_mode == INotifyMode) { + if ( e->m_status == Normal ) { + (void) inotify_rm_watch( m_inotify_fd, e->wd ); + kdDebug(7001) << "Cancelled INotify (fd " << + m_inotify_fd << ", " << e->wd << + ") for " << e->path << endl; + } + else { + if (e->isDir) + removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); + else + removeEntry(0, QFileInfo(e->path).dirPath(true), e); + } + } +#endif + +#ifdef HAVE_DNOTIFY + if (e->m_mode == DNotifyMode) { + if (!e->isDir) { + removeEntry(0, QFileInfo(e->path).dirPath(true), e); + } + else { // isDir + // must close the FD. + if ( e->m_status == Normal) { + if (e->dn_fd) { + ::close(e->dn_fd); + fd_Entry.remove(e->dn_fd); + + kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd + << ") for " << e->path << endl; + e->dn_fd = 0; + + } + } + else { + removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e); + } + } + } +#endif + + if (e->m_mode == StatMode) { + statEntries--; + if ( statEntries == 0 ) { + timer->stop(); // stop timer if lists are empty + kdDebug(7001) << " Stopped Polling Timer" << endl; + } + } + + kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path + << (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString("")) + << (instance ? QString(" [%1]").arg(instance->name()) : QString("")) + << endl; + m_mapEntries.remove( e->path ); // <e> not valid any more +} + + +/* Called from KDirWatch destructor: + * remove <instance> as client from all entries + */ +void KDirWatchPrivate::removeEntries( KDirWatch* instance ) +{ + QPtrList<Entry> list; + int minfreq = 3600000; + + // put all entries where instance is a client in list + EntryMap::Iterator it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) { + Client* c = (*it).m_clients.first(); + for(;c;c=(*it).m_clients.next()) + if (c->instance == instance) break; + if (c) { + c->count = 1; // forces deletion of instance as client + list.append(&(*it)); + } + else if ( (*it).m_mode == StatMode && (*it).freq < minfreq ) + minfreq = (*it).freq; + } + + for(Entry* e=list.first();e;e=list.next()) + removeEntry(instance, e->path, 0); + + if (minfreq > freq) { + // we can decrease the global polling frequency + freq = minfreq; + if (timer->isActive()) timer->changeInterval(freq); + kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl; + } +} + +// instance ==0: stop scanning for all instances +bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e) +{ + int stillWatching = 0; + Client* c = e->m_clients.first(); + for(;c;c=e->m_clients.next()) { + if (!instance || instance == c->instance) + c->watchingStopped = true; + else if (!c->watchingStopped) + stillWatching += c->count; + } + + kdDebug(7001) << instance->name() << " stopped scanning " << e->path + << " (now " << stillWatching << " watchers)" << endl; + + if (stillWatching == 0) { + // if nobody is interested, we don't watch + e->m_ctime = invalid_ctime; // invalid + e->m_status = NonExistent; + // e->m_status = Normal; + } + return true; +} + +// instance ==0: start scanning for all instances +bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e, + bool notify) +{ + int wasWatching = 0, newWatching = 0; + Client* c = e->m_clients.first(); + for(;c;c=e->m_clients.next()) { + if (!c->watchingStopped) + wasWatching += c->count; + else if (!instance || instance == c->instance) { + c->watchingStopped = false; + newWatching += c->count; + } + } + if (newWatching == 0) + return false; + + kdDebug(7001) << (instance ? instance->name() : "all") << " restarted scanning " << e->path + << " (now " << wasWatching+newWatching << " watchers)" << endl; + + // restart watching and emit pending events + + int ev = NoChange; + if (wasWatching == 0) { + if (!notify) { + KDE_struct_stat stat_buf; + bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0); + if (exists) { + e->m_ctime = stat_buf.st_ctime; + e->m_status = Normal; + e->m_nlink = stat_buf.st_nlink; + } + else { + e->m_ctime = invalid_ctime; + e->m_status = NonExistent; + e->m_nlink = 0; + } + } + e->msecLeft = 0; + ev = scanEntry(e); + } + emitEvent(e,ev); + + return true; +} + +// instance ==0: stop scanning for all instances +void KDirWatchPrivate::stopScan(KDirWatch* instance) +{ + EntryMap::Iterator it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) + stopEntryScan(instance, &(*it)); +} + + +void KDirWatchPrivate::startScan(KDirWatch* instance, + bool notify, bool skippedToo ) +{ + if (!notify) + resetList(instance,skippedToo); + + EntryMap::Iterator it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) + restartEntryScan(instance, &(*it), notify); + + // timer should still be running when in polling mode +} + + +// clear all pending events, also from stopped +void KDirWatchPrivate::resetList( KDirWatch* /*instance*/, + bool skippedToo ) +{ + EntryMap::Iterator it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) { + + Client* c = (*it).m_clients.first(); + for(;c;c=(*it).m_clients.next()) + if (!c->watchingStopped || skippedToo) + c->pending = NoChange; + } +} + +// Return event happened on <e> +// +int KDirWatchPrivate::scanEntry(Entry* e) +{ +#ifdef HAVE_FAM + if (e->m_mode == FAMMode) { + // we know nothing has changed, no need to stat + if(!e->dirty) return NoChange; + e->dirty = false; + } +#endif + + // Shouldn't happen: Ignore "unknown" notification method + if (e->m_mode == UnknownMode) return NoChange; + +#if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY ) + if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) { + // we know nothing has changed, no need to stat + if(!e->dirty) return NoChange; + kdDebug(7001) << "scanning " << e->path << " " << e->m_status << " " << e->m_ctime << endl; + e->dirty = false; + } +#endif + + if (e->m_mode == StatMode) { + // only scan if timeout on entry timer happens; + // e.g. when using 500msec global timer, a entry + // with freq=5000 is only watched every 10th time + + e->msecLeft -= freq; + if (e->msecLeft>0) return NoChange; + e->msecLeft += e->freq; + } + + KDE_struct_stat stat_buf; + bool exists = (KDE_stat(QFile::encodeName(e->path), &stat_buf) == 0); + if (exists) { + + if (e->m_status == NonExistent) { + e->m_ctime = stat_buf.st_ctime; + e->m_status = Normal; + e->m_nlink = stat_buf.st_nlink; + return Created; + } + + if ( (e->m_ctime != invalid_ctime) && + ((stat_buf.st_ctime != e->m_ctime) || + (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) { + e->m_ctime = stat_buf.st_ctime; + e->m_nlink = stat_buf.st_nlink; + return Changed; + } + + return NoChange; + } + + // dir/file doesn't exist + + if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) { + e->m_nlink = 0; + e->m_status = NonExistent; + return NoChange; + } + + e->m_ctime = invalid_ctime; + e->m_nlink = 0; + e->m_status = NonExistent; + + return Deleted; +} + +/* Notify all interested KDirWatch instances about a given event on an entry + * and stored pending events. When watching is stopped, the event is + * added to the pending events. + */ +void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName) +{ + QString path = e->path; + if (!fileName.isEmpty()) { + if (!QDir::isRelativePath(fileName)) + path = fileName; + else +#ifdef Q_OS_UNIX + path += "/" + fileName; +#elif defined(Q_WS_WIN) + //current drive is passed instead of / + path += QDir::currentDirPath().left(2) + "/" + fileName; +#endif + } + + QPtrListIterator<Client> cit( e->m_clients ); + for ( ; cit.current(); ++cit ) + { + Client* c = cit.current(); + + if (c->instance==0 || c->count==0) continue; + + if (c->watchingStopped) { + // add event to pending... + if (event == Changed) + c->pending |= event; + else if (event == Created || event == Deleted) + c->pending = event; + continue; + } + // not stopped + if (event == NoChange || event == Changed) + event |= c->pending; + c->pending = NoChange; + if (event == NoChange) continue; + + if (event & Deleted) { + c->instance->setDeleted(path); + // emit only Deleted event... + continue; + } + + if (event & Created) { + c->instance->setCreated(path); + // possible emit Change event after creation + } + + if (event & Changed) + c->instance->setDirty(path); + } +} + +// Remove entries which were marked to be removed +void KDirWatchPrivate::slotRemoveDelayed() +{ + Entry* e; + delayRemove = false; + for(e=removeList.first();e;e=removeList.next()) + removeEntry(0, e->path, 0); + removeList.clear(); +} + +/* Scan all entries to be watched for changes. This is done regularly + * when polling and once after a DNOTIFY signal. This is NOT used by FAM. + */ +void KDirWatchPrivate::slotRescan() +{ + EntryMap::Iterator it; + + // People can do very long things in the slot connected to dirty(), + // like showing a message box. We don't want to keep polling during + // that time, otherwise the value of 'delayRemove' will be reset. + bool timerRunning = timer->isActive(); + if ( timerRunning ) + timer->stop(); + + // We delay deletions of entries this way. + // removeDir(), when called in slotDirty(), can cause a crash otherwise + delayRemove = true; + +#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) + QPtrList<Entry> dList, cList; +#endif + + if (rescan_all) + { + // mark all as dirty + it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) + (*it).dirty = true; + rescan_all = false; + } + else + { + // progate dirty flag to dependant entries (e.g. file watches) + it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) + if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty ) + (*it).propagate_dirty(); + } + + it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) { + // we don't check invalid entries (i.e. remove delayed) + if (!(*it).isValid()) continue; + + int ev = scanEntry( &(*it) ); + + +#ifdef HAVE_INOTIFY + if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) { + cList.append( &(*it) ); + if (! useINotify( &(*it) )) { + useStat( &(*it) ); + } + } +#endif + +#ifdef HAVE_DNOTIFY + if ((*it).m_mode == DNotifyMode) { + if ((*it).isDir && (ev == Deleted)) { + dList.append( &(*it) ); + + // must close the FD. + if ((*it).dn_fd) { + ::close((*it).dn_fd); + fd_Entry.remove((*it).dn_fd); + (*it).dn_fd = 0; + } + } + + else if ((*it).isDir && (ev == Created)) { + // For created, but yet without DNOTIFYing ... + if ( (*it).dn_fd == 0) { + cList.append( &(*it) ); + if (! useDNotify( &(*it) )) { + // if DNotify setup fails... + useStat( &(*it) ); + } + } + } + } +#endif + + if ( ev != NoChange ) + emitEvent( &(*it), ev); + } + + +#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY) + // Scan parent of deleted directories for new creation + Entry* e; + for(e=dList.first();e;e=dList.next()) + addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true); + + // Remove watch of parent of new created directories + for(e=cList.first();e;e=cList.next()) + removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e); +#endif + + if ( timerRunning ) + timer->start(freq); + + QTimer::singleShot(0, this, SLOT(slotRemoveDelayed())); +} + +bool KDirWatchPrivate::isNoisyFile( const char * filename ) +{ + // $HOME/.X.err grows with debug output, so don't notify change + if ( *filename == '.') { + if (strncmp(filename, ".X.err", 6) == 0) return true; + if (strncmp(filename, ".xsession-errors", 16) == 0) return true; + // fontconfig updates the cache on every KDE app start + // (inclusive kio_thumbnail slaves) + if (strncmp(filename, ".fonts.cache", 12) == 0) return true; + } + + return false; +} + +#ifdef HAVE_FAM +void KDirWatchPrivate::famEventReceived() +{ + static FAMEvent fe; + + delayRemove = true; + + while(use_fam && FAMPending(&fc)) { + if (FAMNextEvent(&fc, &fe) == -1) { + kdWarning(7001) << "FAM connection problem, switching to polling." + << endl; + use_fam = false; + delete sn; sn = 0; + + // Replace all FAMMode entries with DNotify/Stat + EntryMap::Iterator it; + it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) + if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) { +#ifdef HAVE_INOTIFY + if (useINotify( &(*it) )) continue; +#endif +#ifdef HAVE_DNOTIFY + if (useDNotify( &(*it) )) continue; +#endif + useStat( &(*it) ); + } + } + else + checkFAMEvent(&fe); + } + + QTimer::singleShot(0, this, SLOT(slotRemoveDelayed())); +} + +void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe) +{ + // Don't be too verbose ;-) + if ((fe->code == FAMExists) || + (fe->code == FAMEndExist) || + (fe->code == FAMAcknowledge)) return; + + if ( isNoisyFile( fe->filename ) ) + return; + + Entry* e = 0; + EntryMap::Iterator it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) + if (FAMREQUEST_GETREQNUM(&( (*it).fr )) == + FAMREQUEST_GETREQNUM(&(fe->fr)) ) { + e = &(*it); + break; + } + + // Entry* e = static_cast<Entry*>(fe->userdata); + +#if 0 // #88538 + kdDebug(7001) << "Processing FAM event (" + << ((fe->code == FAMChanged) ? "FAMChanged" : + (fe->code == FAMDeleted) ? "FAMDeleted" : + (fe->code == FAMStartExecuting) ? "FAMStartExecuting" : + (fe->code == FAMStopExecuting) ? "FAMStopExecuting" : + (fe->code == FAMCreated) ? "FAMCreated" : + (fe->code == FAMMoved) ? "FAMMoved" : + (fe->code == FAMAcknowledge) ? "FAMAcknowledge" : + (fe->code == FAMExists) ? "FAMExists" : + (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code") + << ", " << fe->filename + << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr)) + << ")" << endl; +#endif + + if (!e) { + // this happens e.g. for FAMAcknowledge after deleting a dir... + // kdDebug(7001) << "No entry for FAM event ?!" << endl; + return; + } + + if (e->m_status == NonExistent) { + kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl; + return; + } + + // Delayed handling. This rechecks changes with own stat calls. + e->dirty = true; + if (!rescan_timer.isActive()) + rescan_timer.start(m_PollInterval, true); + + // needed FAM control actions on FAM events + if (e->isDir) + switch (fe->code) + { + case FAMDeleted: + // file absolute: watched dir + if (!QDir::isRelativePath(fe->filename)) + { + // a watched directory was deleted + + e->m_status = NonExistent; + FAMCancelMonitor(&fc, &(e->fr) ); // needed ? + kdDebug(7001) << "Cancelled FAMReq " + << FAMREQUEST_GETREQNUM(&(e->fr)) + << " for " << e->path << endl; + // Scan parent for a new creation + addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true); + } + break; + + case FAMCreated: { + // check for creation of a directory we have to watch + Entry *sub_entry = e->m_entries.first(); + for(;sub_entry; sub_entry = e->m_entries.next()) + if (sub_entry->path == e->path + "/" + fe->filename) break; + if (sub_entry && sub_entry->isDir) { + QString path = e->path; + removeEntry(0,e->path,sub_entry); // <e> can be invalid here!! + sub_entry->m_status = Normal; + if (!useFAM(sub_entry)) +#ifdef HAVE_INOTIFY + if (!useINotify(sub_entry )) +#endif + useStat(sub_entry); + } + break; + } + + default: + break; + } +} +#else +void KDirWatchPrivate::famEventReceived() {} +#endif + + +void KDirWatchPrivate::statistics() +{ + EntryMap::Iterator it; + + kdDebug(7001) << "Entries watched:" << endl; + if (m_mapEntries.count()==0) { + kdDebug(7001) << " None." << endl; + } + else { + it = m_mapEntries.begin(); + for( ; it != m_mapEntries.end(); ++it ) { + Entry* e = &(*it); + kdDebug(7001) << " " << e->path << " (" + << ((e->m_status==Normal)?"":"Nonexistent ") + << (e->isDir ? "Dir":"File") << ", using " + << ((e->m_mode == FAMMode) ? "FAM" : + (e->m_mode == INotifyMode) ? "INotify" : + (e->m_mode == DNotifyMode) ? "DNotify" : + (e->m_mode == StatMode) ? "Stat" : "Unknown Method") + << ")" << endl; + + Client* c = e->m_clients.first(); + for(;c; c = e->m_clients.next()) { + QString pending; + if (c->watchingStopped) { + if (c->pending & Deleted) pending += "deleted "; + if (c->pending & Created) pending += "created "; + if (c->pending & Changed) pending += "changed "; + if (!pending.isEmpty()) pending = " (pending: " + pending + ")"; + pending = ", stopped" + pending; + } + kdDebug(7001) << " by " << c->instance->name() + << " (" << c->count << " times)" + << pending << endl; + } + if (e->m_entries.count()>0) { + kdDebug(7001) << " dependent entries:" << endl; + Entry* d = e->m_entries.first(); + for(;d; d = e->m_entries.next()) { + kdDebug(7001) << " " << d << endl; + kdDebug(7001) << " " << d->path << " (" << d << ") " << endl; + } + } + } + } +} + + +// +// Class KDirWatch +// + +static KStaticDeleter<KDirWatch> sd_dw; +KDirWatch* KDirWatch::s_pSelf = 0L; + +KDirWatch* KDirWatch::self() +{ + if ( !s_pSelf ) { + sd_dw.setObject( s_pSelf, new KDirWatch ); + } + + return s_pSelf; +} + +bool KDirWatch::exists() +{ + return s_pSelf != 0; +} + +KDirWatch::KDirWatch (QObject* parent, const char* name) + : QObject(parent,name) +{ + if (!name) { + static int nameCounter = 0; + + nameCounter++; + setName(QString("KDirWatch-%1").arg(nameCounter).ascii()); + } + + if (!dwp_self) + dwp_self = new KDirWatchPrivate; + d = dwp_self; + d->ref(); + + _isStopped = false; +} + +KDirWatch::~KDirWatch() +{ + d->removeEntries(this); + if ( d->deref() ) + { + // delete it if it's the last one + delete d; + dwp_self = 0L; + } +} + + +// TODO: add watchFiles/recursive support +void KDirWatch::addDir( const QString& _path, + bool watchFiles, bool recursive) +{ + if (watchFiles || recursive) { + kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl; + } + if (d) d->addEntry(this, _path, 0, true); +} + +void KDirWatch::addFile( const QString& _path ) +{ + if (d) d->addEntry(this, _path, 0, false); +} + +QDateTime KDirWatch::ctime( const QString &_path ) +{ + KDirWatchPrivate::Entry* e = d->entry(_path); + + if (!e) + return QDateTime(); + + QDateTime result; + result.setTime_t(e->m_ctime); + return result; +} + +void KDirWatch::removeDir( const QString& _path ) +{ + if (d) d->removeEntry(this, _path, 0); +} + +void KDirWatch::removeFile( const QString& _path ) +{ + if (d) d->removeEntry(this, _path, 0); +} + +bool KDirWatch::stopDirScan( const QString& _path ) +{ + if (d) { + KDirWatchPrivate::Entry *e = d->entry(_path); + if (e && e->isDir) return d->stopEntryScan(this, e); + } + return false; +} + +bool KDirWatch::restartDirScan( const QString& _path ) +{ + if (d) { + KDirWatchPrivate::Entry *e = d->entry(_path); + if (e && e->isDir) + // restart without notifying pending events + return d->restartEntryScan(this, e, false); + } + return false; +} + +void KDirWatch::stopScan() +{ + if (d) d->stopScan(this); + _isStopped = true; +} + +void KDirWatch::startScan( bool notify, bool skippedToo ) +{ + _isStopped = false; + if (d) d->startScan(this, notify, skippedToo); +} + + +bool KDirWatch::contains( const QString& _path ) const +{ + KDirWatchPrivate::Entry* e = d->entry(_path); + if (!e) + return false; + + KDirWatchPrivate::Client* c = e->m_clients.first(); + for(;c;c=e->m_clients.next()) + if (c->instance == this) return true; + + return false; +} + +void KDirWatch::statistics() +{ + if (!dwp_self) { + kdDebug(7001) << "KDirWatch not used" << endl; + return; + } + dwp_self->statistics(); +} + + +void KDirWatch::setCreated( const QString & _file ) +{ + kdDebug(7001) << name() << " emitting created " << _file << endl; + emit created( _file ); +} + +void KDirWatch::setDirty( const QString & _file ) +{ + kdDebug(7001) << name() << " emitting dirty " << _file << endl; + emit dirty( _file ); +} + +void KDirWatch::setDeleted( const QString & _file ) +{ + kdDebug(7001) << name() << " emitting deleted " << _file << endl; + emit deleted( _file ); +} + +KDirWatch::Method KDirWatch::internalMethod() +{ +#ifdef HAVE_FAM + if (d->use_fam) + return KDirWatch::FAM; +#endif +#ifdef HAVE_INOTIFY + if (d->supports_inotify) + return KDirWatch::INotify; +#endif +#ifdef HAVE_DNOTIFY + if (d->supports_dnotify) + return KDirWatch::DNotify; +#endif + return KDirWatch::Stat; +} + + +#include "kdirwatch.moc" +#include "kdirwatch_p.moc" + +//sven + +// vim: sw=2 ts=8 et diff --git a/kio/kio/krun.cpp b/kio/kio/krun.cpp index b90f603f0..6acfb3b74 100644 --- a/kio/kio/krun.cpp +++ b/kio/kio/krun.cpp @@ -77,6 +77,7 @@ public: QString m_localPath; QString m_suggestedFileName; QGuardedPtr <QWidget> m_window; + QCString m_asn; }; pid_t KRun::runURL( const KURL& u, const QString& _mimetype ) @@ -109,14 +110,20 @@ bool KRun::isExecutableFile( const KURL& url, const QString &mimetype ) return false; } -// This is called by foundMimeType, since it knows the mimetype of the URL pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile, bool runExecutables, const QString& suggestedFileName ) { + return runURL( u, _mimetype, NULL, "", tempFile, runExecutables, suggestedFileName ); +} + +// This is called by foundMimeType, since it knows the mimetype of the URL +pid_t KRun::runURL( const KURL& u, const QString& _mimetype, QWidget* window, const QCString& asn, + bool tempFile, bool runExecutables, const QString& suggestedFileName ) +{ bool noRun = false; bool noAuth = false; if ( _mimetype == "inode/directory-locked" ) { - KMessageBoxWrapper::error( 0L, + KMessageBoxWrapper::error( window, i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>").arg(u.htmlURL()) ); return 0; } @@ -133,7 +140,7 @@ pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile, bool { QString path = u.path(); shellQuote( path ); - return (KRun::runCommand(path)); // just execute the url as a command + return (KRun::runCommand(path, QString::null, QString::null, window, asn)); // just execute the url as a command // ## TODO implement deleting the file if tempFile==true } else @@ -155,14 +162,14 @@ pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile, bool if ( noRun ) { - KMessageBox::sorry( 0L, + KMessageBox::sorry( window, i18n("<qt>The file <b>%1</b> is an executable program. " "For safety it will not be started.</qt>").arg(u.htmlURL())); return 0; } if ( noAuth ) { - KMessageBoxWrapper::error( 0L, + KMessageBoxWrapper::error( window, i18n("<qt>You do not have permission to run <b>%1</b>.</qt>").arg(u.htmlURL()) ); return 0; } @@ -182,7 +189,7 @@ pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile, bool return displayOpenWithDialog( lst, tempFile, suggestedFileName ); } - return KRun::run( *offer, lst, 0 /*window*/, tempFile, suggestedFileName ); + return KRun::run( *offer, lst, window, asn, tempFile, suggestedFileName ); } bool KRun::displayOpenWithDialog( const KURL::List& lst ) @@ -536,13 +543,13 @@ QString KRun::binaryName( const QString & execLine, bool removePath ) } static pid_t runCommandInternal( KProcess* proc, const KService* service, const QString& binName, - const QString &execName, const QString & iconName ) + const QString &execName, const QString & iconName, QWidget* window, QCString asn ) { if (service && !service->desktopEntryPath().isEmpty() && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() )) { kdWarning() << "No authorization to execute " << service->desktopEntryPath() << endl; - KMessageBox::sorry(0, i18n("You are not authorized to execute this file.")); + KMessageBox::sorry(window, i18n("You are not authorized to execute this file.")); return 0; } QString bin = KRun::binaryName( binName, true ); @@ -550,10 +557,10 @@ static pid_t runCommandInternal( KProcess* proc, const KService* service, const bool silent; QCString wmclass; KStartupInfoId id; - bool startup_notify = KRun::checkStartupNotify( binName, service, &silent, &wmclass ); + bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass )); if( startup_notify ) { - id.initId(); + id.initId( asn ); id.setupStartupEnv(); KStartupInfoData data; data.setHostname(); @@ -572,6 +579,8 @@ static pid_t runCommandInternal( KProcess* proc, const KService* service, const if( silent ) data.setSilent( KStartupInfoData::Yes ); data.setDesktop( KWin::currentDesktop()); + if( window ) + data.setLaunchedBy( window->winId()); KStartupInfo::sendStartup( id, data ); } pid_t pid = KProcessRunner::run( proc, binName, id ); @@ -635,7 +644,8 @@ bool KRun::checkStartupNotify( const QString& /*binName*/, const KService* servi return true; } -static pid_t runTempService( const KService& _service, const KURL::List& _urls, bool tempFiles, const QString& suggestedFileName ) +static pid_t runTempService( const KService& _service, const KURL::List& _urls, QWidget* window, + const QCString& asn, bool tempFiles, const QString& suggestedFileName ) { if (!_urls.isEmpty()) { kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl; @@ -654,7 +664,7 @@ static pid_t runTempService( const KService& _service, const KURL::List& _urls, { KURL::List singleUrl; singleUrl.append(*it); - runTempService( _service, singleUrl, tempFiles, suggestedFileName ); + runTempService( _service, singleUrl, window, "", tempFiles, suggestedFileName ); } KURL::List singleUrl; singleUrl.append(_urls.first()); @@ -673,7 +683,7 @@ static pid_t runTempService( const KService& _service, const KURL::List& _urls, proc->setWorkingDirectory(_service.path()); return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ), - _service.name(), _service.icon() ); + _service.name(), _service.icon(), window, asn ); } // WARNING: don't call this from processDesktopExec, since klauncher uses that too... @@ -734,11 +744,22 @@ pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFil pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, bool tempFiles ) { - return run( _service, _urls, window, tempFiles, QString::null ); + return run( _service, _urls, window, "", tempFiles, QString::null ); +} + +pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, const QCString& asn, bool tempFiles ) +{ + return run( _service, _urls, window, asn, tempFiles, QString::null ); } pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, bool tempFiles, const QString& suggestedFileName ) { + return run( _service, _urls, window, "", tempFiles, suggestedFileName ); +} + +pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* window, const QCString& asn, + bool tempFiles, const QString& suggestedFileName ) +{ if (!_service.desktopEntryPath().isEmpty() && !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath())) { @@ -759,7 +780,7 @@ pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* win if ( tempFiles || _service.desktopEntryPath().isEmpty() || !suggestedFileName.isEmpty() ) { - return runTempService(_service, _urls, tempFiles, suggestedFileName); + return runTempService(_service, _urls, window, asn, tempFiles, suggestedFileName); } kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl; @@ -773,9 +794,25 @@ pid_t KRun::run( const KService& _service, const KURL::List& _urls, QWidget* win QString error; int pid = 0; - + + QCString myasn = asn; + // startServiceByDesktopPath() doesn't take QWidget*, add it to the startup info now + if( window != NULL ) + { + if( myasn.isEmpty()) + myasn = KStartupInfo::createNewStartupId(); + if( myasn != "0" ) + { + KStartupInfoId id; + id.initId( myasn ); + KStartupInfoData data; + data.setLaunchedBy( window->winId()); + KStartupInfo::sendChange( id, data ); + } + } + int i = KApplication::startServiceByDesktopPath( - _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid + _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid, myasn ); if (i != 0) @@ -800,33 +837,47 @@ pid_t KRun::run( const QString& _exec, const KURL::List& _urls, const QString& _ pid_t KRun::runCommand( QString cmd ) { - return KRun::runCommand( cmd, QString::null, QString::null ); + return KRun::runCommand( cmd, QString::null, QString::null, NULL, "" ); } pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName ) { + return KRun::runCommand( cmd, execName, iconName, NULL, "" ); +} + +pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName, + QWidget* window, const QCString& asn ) +{ kdDebug(7010) << "runCommand " << cmd << "," << execName << endl; KProcess * proc = new KProcess; proc->setUseShell(true); *proc << cmd; KService::Ptr service = KService::serviceByDesktopName( binaryName( execName, true ) ); - return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName ); + return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName, + window, asn ); } KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo ) :m_timer(0,"KRun::timer") { - init (url, 0, mode, isLocalFile, showProgressInfo); + init (url, 0, "", mode, isLocalFile, showProgressInfo); } KRun::KRun( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile, bool showProgressInfo ) :m_timer(0,"KRun::timer") { - init (url, window, mode, isLocalFile, showProgressInfo); + init (url, window, "", mode, isLocalFile, showProgressInfo); +} + +KRun::KRun( const KURL& url, QWidget* window, const QCString& asn, mode_t mode, bool isLocalFile, + bool showProgressInfo ) + :m_timer(0,"KRun::timer") +{ + init (url, window, asn, mode, isLocalFile, showProgressInfo); } -void KRun::init ( const KURL& url, QWidget* window, mode_t mode, bool isLocalFile, +void KRun::init ( const KURL& url, QWidget* window, const QCString& asn, mode_t mode, bool isLocalFile, bool showProgressInfo ) { m_bFault = false; @@ -842,6 +893,7 @@ void KRun::init ( const KURL& url, QWidget* window, mode_t mode, bool isLocalFil d = new KRunPrivate; d->m_runExecutables = true; d->m_window = window; + d->m_asn = asn; setEnableExternalBrowser(true); // Start the timer. This means we will return to the event @@ -942,7 +994,7 @@ void KRun::init() KService::Ptr service = KService::serviceByStorageId( exec ); if (service) { - run( *service, urls ); + run( *service, urls, d->m_window, d->m_asn ); ok = true; } } @@ -1235,7 +1287,7 @@ void KRun::foundMimeType( const QString& type ) { KURL::List lst; lst.append( m_strURL ); - m_bFinished = KRun::run( *serv, lst ); + m_bFinished = KRun::run( *serv, lst, d->m_window, d->m_asn ); /// Note: the line above means that if that service failed, we'll /// go to runURL to maybe find another service, even though a dialog /// box was displayed. That's good if runURL tries another service, @@ -1250,7 +1302,7 @@ void KRun::foundMimeType( const QString& type ) m_strURL.setPath( d->m_localPath ); } - if (!m_bFinished && KRun::runURL( m_strURL, type, false, d->m_runExecutables, d->m_suggestedFileName )){ + if (!m_bFinished && KRun::runURL( m_strURL, type, d->m_window, d->m_asn, false, d->m_runExecutables, d->m_suggestedFileName )){ m_bFinished = true; } else{ diff --git a/kio/kio/krun.h b/kio/kio/krun.h index c4f9bee0e..385b227af 100644 --- a/kio/kio/krun.h +++ b/kio/kio/krun.h @@ -111,6 +111,8 @@ public: */ KRun( const KURL& url, QWidget* window, mode_t mode = 0, bool isLocalFile = false, bool showProgressInfo = true ); + KRun( const KURL& url, QWidget* window, const QCString& asn, mode_t mode = 0, + bool isLocalFile = false, bool showProgressInfo = true ); /** * Destructor. Don't call it yourself, since a KRun object auto-deletes @@ -210,6 +212,8 @@ public: * @since 3.5.2 */ static pid_t run( const KService& _service, const KURL::List& _urls, QWidget* window, bool tempFiles = false ); + static pid_t run( const KService& _service, const KURL::List& _urls, QWidget* window, + const QCString& asn, bool tempFiles = false ); /** * Open a list of URLs with a certain service (application). * @@ -226,6 +230,8 @@ public: /// @since 3.5.3 /// @internal static pid_t run( const KService& _service, const KURL::List& _urls, QWidget* window, bool tempFiles, const QString& suggestedFileName ); + static pid_t run( const KService& _service, const KURL::List& _urls, QWidget* window, + const QCString& asn, bool tempFiles, const QString& suggestedFileName ); /** * Open a list of URLs with. @@ -269,6 +275,7 @@ public: static pid_t runURL( const KURL& _url, const QString& _mimetype ); /// @since 3.5.3 /// @internal + static pid_t runURL( const KURL& _url, const QString& _mimetype, QWidget* window, const QCString& asn, bool tempFile, bool runExecutables, const QString& suggestedFileName ); static pid_t runURL( const KURL& _url, const QString& _mimetype, bool tempFile, bool runExecutables, const QString& suggestedFileName ); /** @@ -299,6 +306,8 @@ public: * of running command) if command was unsafe for map notification. */ static pid_t runCommand( const QString& cmd, const QString & execName, const QString & icon ); + static pid_t runCommand( const QString& cmd, const QString & execName, const QString & icon, + QWidget* window, const QCString& asn ); /** * Display the Open-With dialog for those URLs, and run the chosen application. @@ -438,7 +447,7 @@ protected: virtual void virtual_hook( int id, void* data ); private: - void init (const KURL& url, QWidget* window, mode_t mode, + void init (const KURL& url, QWidget* window, const QCString& asn, mode_t mode, bool isLocalFile, bool showProgressInfo); private: class KRunPrivate; diff --git a/kio/kio/kservice.cpp b/kio/kio/kservice.cpp index bfb5ba2e7..d33e33530 100644 --- a/kio/kio/kservice.cpp +++ b/kio/kio/kservice.cpp @@ -97,6 +97,7 @@ KService::init( KDesktopFile *config ) m_bValid = true; bool absPath = !QDir::isRelativePath(entryPath()); + bool kde4application = config->fileName().startsWith("/usr/share/applications/kde4/"); config->setDesktopGroup(); @@ -196,9 +197,28 @@ KService::init( KDesktopFile *config ) name = name.left(pos); m_strExec = config->readPathEntry( "Exec" ); + if (kde4application && !m_strExec.startsWith("/")) { + m_strExec = "KDEHOME=$HOME/.kde XDG_DATA_DIRS=/usr/share KDEDIRS=/usr/ XDG_CONFIG_DIRS=/etc/xdg/ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$PATH "+m_strExec; + } else if (config->readBoolEntry("X-KDE-SubstituteUID")) { + int space = m_strExec.find(" "); + if (space==-1) + m_strExec = KStandardDirs::findExe(m_strExec); + else { + const QString command = m_strExec.left(space); + m_strExec.replace(command,KStandardDirs::findExe(command)); + } + } + entryMap.remove("Exec"); m_strIcon = config->readEntry( "Icon", "unknown" ); + if (kde4application) { + if (QFile::exists("/usr/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png")) { + m_strIcon = "/usr/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png"; + } else if (QFile::exists("/usr/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png")) { + m_strIcon = "/usr/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png"; + } + } entryMap.remove("Icon"); m_bTerminal = (config->readBoolEntry( "Terminal" )); // should be a property IMHO entryMap.remove("Terminal"); @@ -209,6 +229,9 @@ KService::init( KDesktopFile *config ) m_strComment = config->readEntry( "Comment" ); entryMap.remove("Comment"); m_strGenName = config->readEntry( "GenericName" ); + if (kde4application) { + m_strGenName += " [KDE4]"; + } entryMap.remove("GenericName"); QString untranslatedGenericName = config->readEntryUntranslated( "GenericName" ); if (!untranslatedGenericName.isEmpty()) @@ -226,7 +249,8 @@ KService::init( KDesktopFile *config ) m_lstServiceTypes = config->readListEntry( "ServiceTypes" ); entryMap.remove("ServiceTypes"); // For compatibility with KDE 1.x - m_lstServiceTypes += config->readListEntry( "MimeType", ';' ); + if (!kde4application) + m_lstServiceTypes += config->readListEntry( "MimeType", ';' ); entryMap.remove("MimeType"); if ( m_strType == "Application" && !m_lstServiceTypes.contains("Application") ) @@ -245,6 +269,8 @@ KService::init( KDesktopFile *config ) m_DCOPServiceType = DCOP_None; m_strDesktopEntryName = name.lower(); + if (kde4application) + m_strDesktopEntryName = "kde4-" + m_strDesktopEntryName; m_bAllowAsDefault = config->readBoolEntry( "AllowDefault", true ); entryMap.remove("AllowDefault"); @@ -260,7 +286,10 @@ KService::init( KDesktopFile *config ) for( ; it != entryMap.end();++it) { //qDebug(" Key = %s Data = %s", it.key().latin1(), it.data().latin1()); - m_mapProps.insert( it.key(), QVariant( it.data())); + QString key = it.key(); + if (kde4application && key=="OnlyShowIn" && it.data()=="KDE;") + key = "NotShowIn"; + m_mapProps.insert( key, QVariant( it.data())); } } diff --git a/kio/kio/kurlcompletion.cpp b/kio/kio/kurlcompletion.cpp index 72a41d9a3..cddd9fa79 100644 --- a/kio/kio/kurlcompletion.cpp +++ b/kio/kio/kurlcompletion.cpp @@ -231,6 +231,9 @@ void DirectoryListThread::run() while ( !terminationRequested() && (dirEntry = ::readdir( dir))) #else +#if !defined(MAXPATHLEN) && defined(__GNU__) +#define MAXPATHLEN UCHAR_MAX +#endif struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 ); struct dirent *dirEntry = 0; while ( !terminationRequested() && diff --git a/kio/kpasswdserver/Makefile.am b/kio/kpasswdserver/Makefile.am index fcc3e2b1b..4ddb85be4 100644 --- a/kio/kpasswdserver/Makefile.am +++ b/kio/kpasswdserver/Makefile.am @@ -10,6 +10,6 @@ kde_module_LTLIBRARIES = kded_kpasswdserver.la kded_kpasswdserver_la_SOURCES = kpasswdserver.cpp kpasswdserver.skel kded_kpasswdserver_la_METASOURCES = AUTO kded_kpasswdserver_la_LDFLAGS = $(all_libraries) -module -avoid-version -kded_kpasswdserver_la_LIBADD = $(top_builddir)/kwallet/client/libkwalletclient.la $(LIB_KIO) $(LIB_KDED) +kded_kpasswdserver_la_LIBADD = $(top_builddir)/kwallet/client/libkwalletclient.la $(LIB_KIO) $(LIB_KDED) $(LIB_QT) $(LIB_KDEUI) $(LIB_KDECORE) $(top_builddir)/dcop/libDCOP.la $(LIB_X11) $(top_builddir)/kded/libkdeinit_kded.la include $(top_srcdir)/admin/Doxyfile.am diff --git a/kio/misc/Makefile.am b/kio/misc/Makefile.am index 5dab3a23e..6dc6bcd60 100644 --- a/kio/misc/Makefile.am +++ b/kio/misc/Makefile.am @@ -29,7 +29,7 @@ bin_PROGRAMS = kmailservice ktelnetservice observer_DIR = $(top_srcdir)/kio/kio kio_uiserver_la_SOURCES = uiserver.cpp uiserver.skel observer.stub -kio_uiserver_la_LIBADD = $(LIB_KIO) +kio_uiserver_la_LIBADD = $(LIB_KIO) $(LIB_KDEUI) $(LIB_QT) $(top_builddir)/dcop/libDCOP.la $(LIB_KDECORE) kio_uiserver_la_LDFLAGS = $(all_libraries) -module -avoid-version METASOURCES = AUTO diff --git a/kio/misc/fileshareset b/kio/misc/fileshareset index f73e5f008..4c921ba38 100755 --- a/kio/misc/fileshareset +++ b/kio/misc/fileshareset @@ -285,7 +285,12 @@ sub update_server { system('PATH=/bin:/sbin pidof nfsd >/dev/null') != 0) { # trying to start the server... system('/etc/init.d/portmap start') if system('/etc/init.d/portmap status') != 0; - system('/etc/init.d/nfs', $_) foreach 'stop', 'start'; + if ( -f '/etc/init.d/nfs' ) { + system('/etc/init.d/nfs', $_) foreach 'stop', 'start'; + } + elsif ( -f '/etc/init.d/nfs-kernel-server' ) { + system('/etc/init.d/nfs-kernel-server', $_) foreach 'stop', 'start'; + } } exit 0; } diff --git a/kio/misc/kdesasl/Makefile.am b/kio/misc/kdesasl/Makefile.am index 854757d37..4020a966c 100644 --- a/kio/misc/kdesasl/Makefile.am +++ b/kio/misc/kdesasl/Makefile.am @@ -8,5 +8,5 @@ kdesaslinclude_HEADERS = kdesasl.h libkdesasl_la_SOURCES = kdesasl.cpp libkdesasl_la_LDFLAGS = $(all_libraries) -version-info 3:0:2 -no-undefined -libkdesasl_la_LIBADD = $(LIB_KDECORE) +libkdesasl_la_LIBADD = $(LIB_KDECORE) $(LIB_QT) diff --git a/kio/misc/kntlm/Makefile.am b/kio/misc/kntlm/Makefile.am index 192ddc0af..7136310d9 100644 --- a/kio/misc/kntlm/Makefile.am +++ b/kio/misc/kntlm/Makefile.am @@ -8,5 +8,5 @@ kntlminclude_HEADERS = kntlm.h libkntlm_la_SOURCES = kntlm.cpp des.cpp libkntlm_la_LDFLAGS = $(all_libraries) -version-info 0:0:0 -no-undefined -libkntlm_la_LIBADD = $(LIB_KDECORE) +libkntlm_la_LIBADD = $(LIB_KDECORE) $(LIB_QT) diff --git a/kio/misc/kpac/Makefile.am b/kio/misc/kpac/Makefile.am index 173071318..33d7d0196 100644 --- a/kio/misc/kpac/Makefile.am +++ b/kio/misc/kpac/Makefile.am @@ -10,7 +10,9 @@ KDE_CXXFLAGS = $(USE_EXCEPTIONS) kded_proxyscout_la_SOURCES = proxyscout.skel proxyscout.cpp script.cpp \ downloader.cpp discovery.cpp kded_proxyscout_la_LDFLAGS = $(all_libraries) -module -avoid-version -kded_proxyscout_la_LIBADD = $(LIB_KIO) $(top_builddir)/kjs/libkjs.la $(LIB_KDED) +kded_proxyscout_la_LIBADD = $(LIB_KIO) $(top_builddir)/kjs/libkjs.la $(LIB_KDED) $(LIB_QT) \ + $(top_builddir)/dcop/libDCOP.la $(top_builddir)/kded/libkdeinit_kded.la \ + $(LIB_KDECORE) $(LIBRESOLV) kpac_dhcp_helper_SOURCES = kpac_dhcp_helper.c kpac_dhcp_helper_CFLAGS = $(KDE_USE_FPIE) diff --git a/kio/misc/kssld/Makefile.am b/kio/misc/kssld/Makefile.am index 63252f46c..24faf2e28 100644 --- a/kio/misc/kssld/Makefile.am +++ b/kio/misc/kssld/Makefile.am @@ -21,7 +21,7 @@ INCLUDES= -I$(srcdir)/.. -I$(top_srcdir) $(SSL_INCLUDES) -I$(top_srcdir)/kio/kss kde_module_LTLIBRARIES = kded_kssld.la kded_kssld_la_LDFLAGS = $(all_libraries) -module -avoid-version -kded_kssld_la_LIBADD = $(LIB_KIO) $(LIB_KDED) +kded_kssld_la_LIBADD = $(LIB_KIO) $(LIB_KDED) $(LIB_QT) $(top_builddir)/dcop/libDCOP.la $(top_builddir)/kded/libkdeinit_kded.la $(LIB_KDECORE) kded_kssld_la_SOURCES = kssld.cpp kssld.skel METASOURCES = AUTO diff --git a/kio/misc/kwalletd/Makefile.am b/kio/misc/kwalletd/Makefile.am index f57f2c692..1c198c46e 100644 --- a/kio/misc/kwalletd/Makefile.am +++ b/kio/misc/kwalletd/Makefile.am @@ -21,7 +21,7 @@ INCLUDES= -I$(srcdir)/.. -I$(top_srcdir) -I$(top_srcdir)/kwallet/backend -I$(top kde_module_LTLIBRARIES = kded_kwalletd.la kded_kwalletd_la_LDFLAGS = $(all_libraries) -module -avoid-version -kded_kwalletd_la_LIBADD = $(LIB_KIO) ../../../kwallet/backend/libkwalletbackend.la ../../../kwallet/client/libkwalletclient.la $(LIB_KDED) +kded_kwalletd_la_LIBADD = $(LIB_KIO) ../../../kwallet/backend/libkwalletbackend.la ../../../kwallet/client/libkwalletclient.la $(LIB_KDED) $(LIB_QT) $(top_builddir)/dcop/libDCOP.la $(LIB_KDECORE) $(LIB_KDEUI) $(top_builddir)/kded/libkdeinit_kded.la kded_kwalletd_la_SOURCES = kwalletd.cpp kwalletd.skel ktimeout.cpp kwalletwizard.ui kbetterthankdialogbase.ui METASOURCES = AUTO diff --git a/kio/misc/kwalletd/kwalletd.cpp b/kio/misc/kwalletd/kwalletd.cpp index 686a99d6f..3fa32ff71 100644 --- a/kio/misc/kwalletd/kwalletd.cpp +++ b/kio/misc/kwalletd/kwalletd.cpp @@ -829,6 +829,12 @@ bool KWalletD::removeFolder(int handle, const QString& f) { if ((b = getWallet(friendlyDCOPPeerName(), handle))) { bool rc = b->removeFolder(f); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); QByteArray data; QDataStream ds(data, IO_WriteOnly); ds << b->walletName(); @@ -845,6 +851,12 @@ bool KWalletD::createFolder(int handle, const QString& f) { if ((b = getWallet(friendlyDCOPPeerName(), handle))) { bool rc = b->createFolder(f); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); QByteArray data; QDataStream ds(data, IO_WriteOnly); ds << b->walletName(); @@ -987,6 +999,12 @@ int KWalletD::writeMap(int handle, const QString& folder, const QString& key, co e.setValue(value); e.setType(KWallet::Wallet::Map); b->writeEntry(&e); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); emitFolderUpdated(b->walletName(), folder); return 0; } @@ -1005,6 +1023,12 @@ int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, e.setValue(value); e.setType(KWallet::Wallet::EntryType(entryType)); b->writeEntry(&e); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); emitFolderUpdated(b->walletName(), folder); return 0; } @@ -1023,6 +1047,12 @@ int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, e.setValue(value); e.setType(KWallet::Wallet::Stream); b->writeEntry(&e); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); emitFolderUpdated(b->walletName(), folder); return 0; } @@ -1041,6 +1071,12 @@ int KWalletD::writePassword(int handle, const QString& folder, const QString& ke e.setValue(value); e.setType(KWallet::Wallet::Password); b->writeEntry(&e); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); emitFolderUpdated(b->walletName(), folder); return 0; } @@ -1090,6 +1126,12 @@ int KWalletD::removeEntry(int handle, const QString& folder, const QString& key) } b->setFolder(folder); bool rc = b->removeEntry(key); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); emitFolderUpdated(b->walletName(), folder); return rc ? 0 : -3; } @@ -1183,6 +1225,12 @@ int KWalletD::renameEntry(int handle, const QString& folder, const QString& oldN if ((b = getWallet(friendlyDCOPPeerName(), handle))) { b->setFolder(folder); int rc = b->renameEntry(oldName, newName); + // write changes to disk immediately + QByteArray p; + QString wallet = b->walletName(); + p.duplicate(_passwords[wallet].data(), _passwords[wallet].length()); + b->sync(p); + p.fill(0); emitFolderUpdated(b->walletName(), folder); return rc; } |