summaryrefslogtreecommitdiffstats
path: root/kmail/editorwatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/editorwatcher.cpp')
-rw-r--r--kmail/editorwatcher.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/kmail/editorwatcher.cpp b/kmail/editorwatcher.cpp
new file mode 100644
index 000000000..9e1bb150f
--- /dev/null
+++ b/kmail/editorwatcher.cpp
@@ -0,0 +1,181 @@
+/*
+ Copyright (c) 2007 Volker Krause <vkrause@kde.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include "editorwatcher.h"
+
+#include <config.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kopenwith.h>
+#include <kprocess.h>
+#include <kuserprofile.h>
+
+#include <qsocketnotifier.h>
+
+#include <cassert>
+
+// inotify stuff taken from kdelibs/kio/kio/kdirwatch.cpp
+#ifdef HAVE_SYS_INOTIFY
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <fcntl.h>
+#elif HAVE_INOTIFY
+#include <sys/ioctl.h>
+#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
+
+using namespace KMail;
+
+EditorWatcher::EditorWatcher(const KURL & url, const QString &mimeType, bool openWith, QObject * parent) :
+ QObject( parent ),
+ mUrl( url ),
+ mMimeType( mimeType ),
+ mOpenWith( openWith ),
+ mEditor( 0 ),
+ mHaveInotify( false ),
+ mFileOpen( false ),
+ mEditorRunning( false ),
+ mFileModified( true ), // assume the worst unless we know better
+ mDone( false )
+{
+ assert( mUrl.isLocalFile() );
+ connect( &mTimer, SIGNAL(timeout()), SLOT(checkEditDone()) );
+}
+
+bool EditorWatcher::start()
+{
+ // find an editor
+ KURL::List list;
+ list.append( mUrl );
+ KService::Ptr offer = KServiceTypeProfile::preferredService( mMimeType, "Application" );
+ if ( mOpenWith || !offer ) {
+ KOpenWithDlg dlg( list, i18n("Edit with:"), QString::null, 0 );
+ if ( !dlg.exec() )
+ return false;
+ offer = dlg.service();
+ if ( !offer )
+ return false;
+ }
+
+#ifdef HAVE_INOTIFY
+ // monitor file
+ mInotifyFd = inotify_init();
+ if ( mInotifyFd > 0 ) {
+ mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().latin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
+ if ( mInotifyWatch >= 0 ) {
+ QSocketNotifier *sn = new QSocketNotifier( mInotifyFd, QSocketNotifier::Read, this );
+ connect( sn, SIGNAL(activated(int)), SLOT(inotifyEvent()) );
+ mHaveInotify = true;
+ mFileModified = false;
+ }
+ } else {
+ kdWarning(5006) << k_funcinfo << "Failed to activate INOTIFY!" << endl;
+ }
+#endif
+
+ // start the editor
+ QStringList params = KRun::processDesktopExec( *offer, list, false );
+ mEditor = new KProcess( this );
+ *mEditor << params;
+ connect( mEditor, SIGNAL(processExited(KProcess*)), SLOT(editorExited()) );
+ if ( !mEditor->start() )
+ return false;
+ mEditorRunning = true;
+
+ mEditTime.start();
+ return true;
+}
+
+void EditorWatcher::inotifyEvent()
+{
+ assert( mHaveInotify );
+#ifdef HAVE_INOTIFY
+ int pending = -1;
+ char buffer[4096];
+ ioctl( mInotifyFd, FIONREAD, &pending );
+ while ( pending > 0 ) {
+ int size = read( mInotifyFd, buffer, QMIN( pending, (int)sizeof(buffer) ) );
+ pending -= size;
+ if ( size < 0 )
+ break; // error
+ int offset = 0;
+ while ( size > 0 ) {
+ struct inotify_event *event = (struct inotify_event *) &buffer[offset];
+ size -= sizeof( struct inotify_event ) + event->len;
+ offset += sizeof( struct inotify_event ) + event->len;
+ if ( event->mask & IN_OPEN )
+ mFileOpen = true;
+ if ( event->mask & IN_CLOSE )
+ mFileOpen = false;
+ if ( event->mask & IN_MODIFY )
+ mFileModified = true;
+ }
+ }
+#endif
+ mTimer.start( 500, true );
+
+}
+
+void EditorWatcher::editorExited()
+{
+ mEditorRunning = false;
+ mTimer.start( 500, true );
+}
+
+void EditorWatcher::checkEditDone()
+{
+ if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
+ return;
+ // protect us against double-deletion by calling this method again while
+ // the subeventloop of the message box is running
+ mDone = true;
+ // nobody can edit that fast, we seem to be unable to detect
+ // when the editor will be closed
+ if ( mEditTime.elapsed() <= 3000 ) {
+ KMessageBox::error( 0, i18n("KMail is unable to detect when the choosen editor is closed. "
+ "To avoid data loss, editing the attachment will be aborted."), i18n("Unable to edit attachment") );
+ }
+
+ emit editDone( this );
+ deleteLater();
+}
+
+#include "editorwatcher.moc"