summaryrefslogtreecommitdiffstats
path: root/tdeioslave/media/medianotifier/medianotifier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdeioslave/media/medianotifier/medianotifier.cpp')
-rw-r--r--tdeioslave/media/medianotifier/medianotifier.cpp386
1 files changed, 386 insertions, 0 deletions
diff --git a/tdeioslave/media/medianotifier/medianotifier.cpp b/tdeioslave/media/medianotifier/medianotifier.cpp
new file mode 100644
index 000000000..de6a56645
--- /dev/null
+++ b/tdeioslave/media/medianotifier/medianotifier.cpp
@@ -0,0 +1,386 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2005 Jean-Remy Falleri <jr.falleri@laposte.net>
+ Copyright (c) 2005 Kévin Ottens <ervin ipsquad net>
+
+ 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.
+*/
+
+#include "medianotifier.h"
+
+#include <sys/vfs.h>
+
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqdir.h>
+#include <tqcheckbox.h>
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <krun.h>
+#include <kmessagebox.h>
+#include <kstdguiitem.h>
+#include <kstandarddirs.h>
+
+#include "notificationdialog.h"
+#include "notifiersettings.h"
+#include "notifieraction.h"
+#include "mediamanagersettings.h"
+
+MediaNotifier::MediaNotifier(const TQCString &name) : KDEDModule(name)
+{
+ connectDCOPSignal( "kded", "mediamanager", "mediumAdded(TQString, bool)",
+ "onMediumChange(TQString, bool)", true );
+
+ connectDCOPSignal( "kded", "mediamanager", "mediumChanged(TQString, bool)",
+ "onMediumChange(TQString, bool)", true );
+
+ m_freeTimer = new TQTimer( this );
+ connect( m_freeTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( checkFreeDiskSpace() ) );
+ m_freeTimer->start( 1000*6*2 /* 20 minutes */ );
+ m_freeDialog = 0;
+}
+
+MediaNotifier::~MediaNotifier()
+{
+ disconnectDCOPSignal( "kded", "mediamanager", "mediumAdded(TQString, bool)",
+ "onMediumChange(TQString, bool)" );
+
+ disconnectDCOPSignal( "kded", "mediamanager", "mediumChanged(TQString, bool)",
+ "onMediumChange(TQString, bool)" );
+}
+
+void MediaNotifier::onMediumChange( const TQString &name, bool allowNotification )
+{
+ kdDebug() << "MediaNotifier::onMediumChange( " << name << ", "
+ << allowNotification << ")" << endl;
+
+ if ( !allowNotification )
+ return;
+
+// Update user activity timestamp, otherwise the notification dialog will be shown
+// in the background due to focus stealing prevention. Entering a new media can
+// be seen as a kind of user activity after all. It'd be better to update the timestamp
+// as soon as the media is entered, but it apparently takes some time to get here.
+ kapp->updateUserTimestamp();
+
+ KURL url( "system:/media/"+name );
+
+ TDEIO::SimpleJob *job = TDEIO::stat( url, false );
+ job->setInteractive( false );
+
+ m_allowNotificationMap[job] = allowNotification;
+
+ connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
+ this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) );
+}
+
+void MediaNotifier::slotStatResult( TDEIO::Job *job )
+{
+ bool allowNotification = m_allowNotificationMap[job];
+ m_allowNotificationMap.remove( job );
+
+ if ( job->error() != 0 ) return;
+
+ TDEIO::StatJob *stat_job = static_cast<TDEIO::StatJob *>( job );
+
+ TDEIO::UDSEntry entry = stat_job->statResult();
+ KURL url = stat_job->url();
+
+ KFileItem medium( entry, url );
+
+ if ( autostart( medium ) ) return;
+
+ if ( allowNotification ) notify( medium );
+}
+
+bool MediaNotifier::autostart( const KFileItem &medium )
+{
+ TQString mimetype = medium.mimetype();
+
+ bool is_cdrom = mimetype.startsWith( "media/cd" ) || mimetype.startsWith( "media/dvd" );
+ bool is_mounted = mimetype.contains( "_mounted" );
+
+ // We autorun only on CD/DVD or removable disks (USB, Firewire)
+ if ( !( is_cdrom || is_mounted )
+ && !mimetype.startsWith("media/removable_mounted") )
+ {
+ return false;
+ }
+
+
+ // Here starts the 'Autostart Of Applications After Mount' implementation
+
+ // The desktop environment MAY ignore Autostart files altogether
+ // based on policy set by the user, system administrator or vendor.
+ MediaManagerSettings::self()->readConfig();
+ if ( !MediaManagerSettings::self()->autostartEnabled() )
+ {
+ return false;
+ }
+
+ // From now we're sure the medium is already mounted.
+ // We can use the local path for stating, no need to use KIO here.
+ bool local;
+ TQString path = medium.mostLocalURL( local ).path(); // local is always true here...
+
+ // When a new medium is mounted the root directory of the medium should
+ // be checked for the following Autostart files in order of precedence:
+ // .autorun, autorun, autorun.sh
+ TQStringList autorun_list;
+ autorun_list << ".autorun" << "autorun" << "autorun.sh";
+
+ TQStringList::iterator it = autorun_list.begin();
+ TQStringList::iterator end = autorun_list.end();
+
+ for ( ; it!=end; ++it )
+ {
+ if ( TQFile::exists( path + "/" + *it ) )
+ {
+ return execAutorun( medium, path, *it );
+ }
+ }
+
+ // When a new medium is mounted the root directory of the medium should
+ // be checked for the following Autoopen files in order of precedence:
+ // .autoopen, autoopen
+ TQStringList autoopen_list;
+ autoopen_list << ".autoopen" << "autoopen";
+
+ it = autoopen_list.begin();
+ end = autoopen_list.end();
+
+ for ( ; it!=end; ++it )
+ {
+ if ( TQFile::exists( path + "/" + *it ) )
+ {
+ return execAutoopen( medium, path, *it );
+ }
+ }
+
+ return false;
+}
+
+bool MediaNotifier::execAutorun( const KFileItem &medium, const TQString &path,
+ const TQString &autorunFile )
+{
+ // The desktop environment MUST prompt the user for confirmation
+ // before automatically starting an application.
+ TQString mediumType = medium.mimeTypePtr()->name();
+ TQString text = i18n( "An autorun file has been found on your '%1'."
+ " Do you want to execute it?\n"
+ "Note that executing a file on a medium may compromise"
+ " your system's security").arg( mediumType );
+ TQString caption = i18n( "Autorun - %1" ).arg( medium.url().prettyURL() );
+ KGuiItem yes = KStdGuiItem::yes();
+ KGuiItem no = KStdGuiItem::no();
+ int options = KMessageBox::Notify | KMessageBox::Dangerous;
+
+ int answer = KMessageBox::warningYesNo( 0L, text, caption, yes, no,
+ TQString::null, options );
+
+ if ( answer == KMessageBox::Yes )
+ {
+ // When an Autostart file has been detected and the user has
+ // confirmed its execution the autostart file MUST be executed
+ // with the current working directory ( CWD ) set to the root
+ // directory of the medium.
+ TDEProcess proc;
+ proc << "sh" << autorunFile;
+ proc.setWorkingDirectory( path );
+ proc.start();
+ proc.detach();
+ }
+
+ return true;
+}
+
+bool MediaNotifier::execAutoopen( const KFileItem &medium, const TQString &path,
+ const TQString &autoopenFile )
+{
+ // An Autoopen file MUST contain a single relative path that points
+ // to a non-executable file contained on the medium. [...]
+ TQFile file( path+"/"+autoopenFile );
+ file.open( IO_ReadOnly );
+ TQTextStream stream( &file );
+
+ TQString relative_path = stream.readLine().stripWhiteSpace();
+
+ // The relative path MUST NOT contain path components that
+ // refer to a parent directory ( ../ )
+ if ( relative_path.startsWith( "/" ) || relative_path.contains( "../" ) )
+ {
+ return false;
+ }
+
+ // The desktop environment MUST verify that the relative path points
+ // to a file that is actually located on the medium [...]
+ TQString resolved_path
+ = KStandardDirs::realFilePath( path+"/"+relative_path );
+
+ if ( !resolved_path.startsWith( path ) )
+ {
+ return false;
+ }
+
+
+ TQFile document( resolved_path );
+
+ // TODO: What about FAT all files are executable...
+ // If the relative path points to an executable file then the desktop
+ // environment MUST NOT execute the file.
+ if ( !document.exists() /*|| TQFileInfo(document).isExecutable()*/ )
+ {
+ return false;
+ }
+
+ KURL url = medium.url();
+ url.addPath( relative_path );
+
+ // The desktop environment MUST prompt the user for confirmation
+ // before opening the file.
+ TQString mediumType = medium.mimeTypePtr()->name();
+ TQString filename = url.filename();
+ TQString text = i18n( "An autoopen file has been found on your '%1'."
+ " Do you want to open '%2'?\n"
+ "Note that opening a file on a medium may compromise"
+ " your system's security").arg( mediumType ).arg( filename );
+ TQString caption = i18n( "Autoopen - %1" ).arg( medium.url().prettyURL() );
+ KGuiItem yes = KStdGuiItem::yes();
+ KGuiItem no = KStdGuiItem::no();
+ int options = KMessageBox::Notify | KMessageBox::Dangerous;
+
+ int answer = KMessageBox::warningYesNo( 0L, text, caption, yes, no,
+ TQString::null, options );
+
+ // TODO: Take case of the "UNLESS" part?
+ // When an Autoopen file has been detected and the user has confirmed
+ // that the file indicated in the Autoopen file should be opened then
+ // the file indicated in the Autoopen file MUST be opened in the
+ // application normally preferred by the user for files of its kind
+ // UNLESS the user instructed otherwise.
+ if ( answer == KMessageBox::Yes )
+ {
+ ( void ) new KRun( url );
+ }
+
+ return true;
+}
+
+void MediaNotifier::notify( KFileItem &medium )
+{
+ kdDebug() << "Notification triggered." << endl;
+
+ NotifierSettings *settings = new NotifierSettings();
+
+ if ( settings->autoActionForMimetype( medium.mimetype() )==0L )
+ {
+ TQValueList<NotifierAction*> actions
+ = settings->actionsForMimetype( medium.mimetype() );
+
+ // If only one action remains, it's the "do nothing" action
+ // no need to popup in this case.
+ if ( actions.size()>1 )
+ {
+ NotificationDialog *dialog
+ = new NotificationDialog( medium, settings );
+ dialog->show();
+ }
+ }
+ else
+ {
+ NotifierAction *action = settings->autoActionForMimetype( medium.mimetype() );
+ action->execute( medium );
+ delete settings;
+ }
+}
+
+extern "C"
+{
+ KDE_EXPORT KDEDModule *create_medianotifier(const TQCString &name)
+ {
+ TDEGlobal::locale()->insertCatalogue("kay");
+ return new MediaNotifier(name);
+ }
+}
+
+void MediaNotifier::checkFreeDiskSpace()
+{
+ struct statfs sfs;
+ long total, avail;
+ if ( m_freeDialog )
+ return;
+
+ if ( statfs( TQFile::encodeName( TQDir::homeDirPath() ), &sfs ) == 0 )
+ {
+ total = sfs.f_blocks;
+ avail = ( getuid() ? sfs.f_bavail : sfs.f_bfree );
+
+ if (avail < 0 || total <= 0)
+ return; // we better do not say anything about it
+
+ int freeperc = static_cast<int>(100 * double(avail) / total);
+
+ if ( freeperc < 5 && KMessageBox::shouldBeShownContinue( "dontagainfreespace" ) ) // free disk space dropped under a limit
+ {
+ m_freeDialog= new KDialogBase(
+ i18n( "Low Disk Space" ),
+ KDialogBase::Yes | KDialogBase::No,
+ KDialogBase::Yes, KDialogBase::No,
+ 0, "warningYesNo", false, true,
+ i18n( "Start Konqueror" ), KStdGuiItem::cancel());
+
+ TQString text = i18n( "You are running low on disk space on your home partition (currently %1% free), would you like to "
+ "run Konqueror to free some disk space and fix the problem?" ).arg( freeperc );
+ bool checkboxResult = false;
+ KMessageBox::createKMessageBox(m_freeDialog, TQMessageBox::Warning, text, TQStringList(),
+ i18n("Do not ask again"),
+ &checkboxResult, KMessageBox::Notify | KMessageBox::NoExec);
+ m_freeDialog->show();
+ connect( m_freeDialog, TQT_SIGNAL( yesClicked() ), TQT_SLOT( slotFreeContinue() ) );
+ connect( m_freeDialog, TQT_SIGNAL( noClicked() ), TQT_SLOT( slotFreeCancel() ) );
+ }
+ }
+}
+
+void MediaNotifier::slotFreeContinue()
+{
+ slotFreeFinished( KMessageBox::Continue );
+}
+
+void MediaNotifier::slotFreeCancel()
+{
+ slotFreeFinished( KMessageBox::Cancel );
+}
+
+void MediaNotifier::slotFreeFinished( KMessageBox::ButtonCode res )
+{
+ TQCheckBox *checkbox = ::tqqt_cast<TQCheckBox*>( m_freeDialog->child( 0, TQCHECKBOX_OBJECT_NAME_STRING ) );
+ if ( checkbox && checkbox->isChecked() )
+ KMessageBox::saveDontShowAgainYesNo("dontagainfreespace", res);
+ m_freeDialog->delayedDestruct();
+ m_freeDialog = 0;
+
+ if ( res == KMessageBox::Continue ) // start Konqi
+ {
+ ( void ) new KRun( KURL::fromPathOrURL( TQDir::homeDirPath() ) );
+ }
+ else // people don't want to be bothered, at least stop the timer; there's no way to save the dontshowagain entry in this case
+ m_freeTimer->stop();
+}
+
+#include "medianotifier.moc"