diff --git a/kded/plugins/sftp/sftp_config.cpp b/kded/plugins/sftp/sftp_config.cpp index ce526b20a..5b1d34c89 100644 --- a/kded/plugins/sftp/sftp_config.cpp +++ b/kded/plugins/sftp/sftp_config.cpp @@ -26,6 +26,7 @@ #include #include +#include "sftpplugin.h" #include "../../kdebugnamespace.h" #include "ui_sftp_config.h" @@ -35,57 +36,67 @@ K_EXPORT_PLUGIN(SftpConfigFactory("kdeconnect_sftp_config", "kdeconnect_sftp_con SftpConfig::SftpConfig(QWidget *parent, const QVariantList& ) : KCModule(SftpConfigFactory::componentData(), parent) - , ui(new Ui::SftpConfigUi()) - , cfg(KSharedConfig::openConfig("kdeconnect/plugins/sftp")) + , m_ui(new Ui::SftpConfigUi()) + , m_cfg(SftpConfig::config()) { - ui->setupUi(this); + m_ui->setupUi(this); - ui->check->setIcon(KIconLoader::global()->loadIcon("view-refresh", KIconLoader::Dialog)); - connect(ui->check, SIGNAL(clicked(bool)), this, SLOT(refresh())); - refresh(); + m_ui->refresh->setIcon(KIconLoader::global()->loadIcon("view-refresh", KIconLoader::Dialog)); + m_ui->pixmap->setPixmap(KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Dialog)); + + connect(m_ui->refresh, SIGNAL(clicked(bool)), this, SLOT(checkSshfs())); + + connect(m_ui->mountpoint, SIGNAL(textChanged(QString)), this, SLOT(changed())); + connect(m_ui->idle, SIGNAL(toggled(bool)), this, SLOT(changed())); + connect(m_ui->timeout, SIGNAL(valueChanged(int)), this, SLOT(changed())); } SftpConfig::~SftpConfig() { - delete ui; } -void SftpConfig::refresh() +void SftpConfig::checkSshfs() { - const QString sshfs = KStandardDirs::findExe("sshfs"); - - if (sshfs.isEmpty()) - { - ui->label->setText(i18n("sshfs not found in PATH")); - ui->pixmap->setPixmap(KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Dialog)); - } - else - { - ui->label->setText(i18n("sshfs found at %1").arg(sshfs)); - ui->pixmap->setPixmap(KIconLoader::global()->loadIcon("dialog-ok", KIconLoader::Dialog)); - } + m_ui->error->setVisible(KStandardDirs::findExe("sshfs").isEmpty()); } void SftpConfig::defaults() { KCModule::defaults(); - refresh(); - //Q_EMIT changed(true); + + checkSshfs(); + m_ui->mountpoint->setUrl(m_cfg->group("main").readEntry("mountpoint" + , KStandardDirs::locateLocal("appdata", "", true, SftpPlugin::componentData()))); + m_ui->idle->setChecked(m_cfg->group("main").readEntry("idle", true)); + m_ui->timeout->setValue(m_cfg->group("main").readEntry("idletimeout", 10)); + + Q_EMIT changed(true); } void SftpConfig::load() { KCModule::load(); - refresh() ; - //Q_EMIT changed(false); + + checkSshfs(); + m_ui->mountpoint->setUrl(m_cfg->group("main").readEntry("mountpoint" + , KStandardDirs::locateLocal("appdata", "", true, SftpPlugin::componentData()))); + m_ui->idle->setChecked(m_cfg->group("main").readEntry("idle", true)); + m_ui->timeout->setValue(m_cfg->group("main").readEntry("idletimeout", 10)); + + Q_EMIT changed(false); } void SftpConfig::save() { - refresh(); + checkSshfs(); + m_cfg->group("main").writeEntry("idle", m_ui->idle->isChecked()); + m_cfg->group("main").writeEntry("idletimeout", m_ui->timeout->value()); + m_cfg->group("main").writeEntry("mountpoint", m_ui->mountpoint->url().url()); + KCModule::save(); - //Q_EMIT changed(false); + Q_EMIT changed(false); } + diff --git a/kded/plugins/sftp/sftp_config.h b/kded/plugins/sftp/sftp_config.h index acab3a909..5ed9b269e 100644 --- a/kded/plugins/sftp/sftp_config.h +++ b/kded/plugins/sftp/sftp_config.h @@ -36,17 +36,22 @@ public: SftpConfig(QWidget *parent, const QVariantList&); virtual ~SftpConfig(); + static inline KSharedConfigPtr config() + { + return KSharedConfig::openConfig("kdeconnect/plugins/sftp"); + } + public Q_SLOTS: virtual void save(); virtual void load(); virtual void defaults(); private Q_SLOTS: - void refresh(); + void checkSshfs(); private: - Ui::SftpConfigUi* ui; - KSharedConfigPtr cfg; + QScopedPointer m_ui; + KSharedConfigPtr m_cfg; }; diff --git a/kded/plugins/sftp/sftp_config.ui b/kded/plugins/sftp/sftp_config.ui index 00e42bf9e..9b01fb7f8 100644 --- a/kded/plugins/sftp/sftp_config.ui +++ b/kded/plugins/sftp/sftp_config.ui @@ -9,41 +9,28 @@ 0 0 - 353 - 68 + 303 + 155 Share plugin settings - - - - - 0 - 0 - - - - - - + + + false - - - - + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + - - Qt::AlignCenter - - + Qt::Vertical @@ -56,15 +43,182 @@ - - + + - + Disconnect when idle + + + true + + + + + + + Error + + + + + + + 0 + 0 + + + + + + + false + + + + + + + sshfsf not found in PATH + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + 0 + 0 + + + + Mountpoint: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 16 + + + 0 + + + + + + 0 + 0 + + + + Timeout: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + min + + + 5 + + + 99999999 + + + 10 + + + + + + + Qt::Horizontal + + + + 53 + 20 + + + + + + + + + + + ... + + + KUrlRequester + QFrame +
kurlrequester.h
+
+
- + + + idle + toggled(bool) + frame + setEnabled(bool) + + + 20 + 98 + + + 14 + 127 + + + + diff --git a/kded/plugins/sftp/sftpplugin.cpp b/kded/plugins/sftp/sftpplugin.cpp index 2400977b5..3cb979240 100644 --- a/kded/plugins/sftp/sftpplugin.cpp +++ b/kded/plugins/sftp/sftpplugin.cpp @@ -21,40 +21,66 @@ #include "sftpplugin.h" #include +#include +#include +#include +#include #include #include #include #include #include #include +#include +#include "sftp_config.h" #include "../../kdebugnamespace.h" K_PLUGIN_FACTORY( KdeConnectPluginFactory, registerPlugin< SftpPlugin >(); ) K_EXPORT_PLUGIN( KdeConnectPluginFactory("kdeconnect_sftp", "kdeconnect_sftp") ) static const char* passwd_c = "sftppassword"; +static const char* mountpoint_c = "sftpmountpoint"; +static const char* timestamp_c = "timestamp"; static const QSet fields_c = QSet() << "ip" << "port" << "user" << "port" << "password" << "path"; +inline bool isTimeout(QObject* o, const KConfigGroup& cfg) +{ + int duration = o->property(timestamp_c).toDateTime().secsTo(QDateTime::currentDateTime()); + return cfg.readEntry("idle", true) && duration > (cfg.readEntry("idletimeout", 60) * 60); +} + +inline QString mountpoint(QObject* o) +{ + return o->property(mountpoint_c).toString(); +} + struct SftpPlugin::Pimpl { - Pimpl() {}; + Pimpl() : waitForMount(false) + { + mountTimer.setSingleShot(true); + }; - QString mountPoint; QPointer mountProc; + QTimer mountTimer; + int idleTimer; + bool waitForMount; }; SftpPlugin::SftpPlugin(QObject *parent, const QVariantList &args) : KdeConnectPlugin(parent, args) , m_d(new Pimpl) { - m_d->mountPoint = KStandardDirs::locateLocal("appdata", device()->name() + "/", true, - KComponentData("kdeconnect", "kdeconnect")); QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents); + + m_d->idleTimer = startTimer(20 * 1000); + + connect(&m_d->mountTimer, SIGNAL(timeout()), this, SLOT(mountTimeout())); } void SftpPlugin::connected() @@ -64,24 +90,49 @@ void SftpPlugin::connected() SftpPlugin::~SftpPlugin() { QDBusConnection::sessionBus().unregisterObject(dbusPath(), QDBusConnection::UnregisterTree); - stopBrowsing(); + umount(); } -void SftpPlugin::startBrowsing() +void SftpPlugin::mount() { + if (m_d->mountTimer.isActive() || m_d->mountProc) + { + return; + } + else + { + m_d->mountTimer.start(10000); + } + NetworkPackage np(PACKAGE_TYPE_SFTP); np.set("startBrowsing", true); device()->sendPackage(np); } -void SftpPlugin::stopBrowsing() +void SftpPlugin::umount() { - cleanMountPoint(); if (m_d->mountProc) { - m_d->mountProc->terminate(); - QTimer::singleShot(5000, m_d->mountProc, SLOT(kill())); - m_d->mountProc->waitForFinished(); + cleanMountPoint(m_d->mountProc); + if (m_d->mountProc) + { + m_d->mountProc->terminate(); + QTimer::singleShot(5000, m_d->mountProc, SLOT(kill())); + m_d->mountProc->waitForFinished(); + } + } +} + +void SftpPlugin::startBrowsing() +{ + if (m_d->mountProc) + { + new KRun(KUrl::fromLocalFile(mountpoint(m_d->mountProc)), 0); + } + else + { + m_d->waitForMount = true; + mount(); } } @@ -95,9 +146,11 @@ bool SftpPlugin::receivePackage(const NetworkPackage& np) if (!m_d->mountProc.isNull()) { - return new KRun(KUrl::fromLocalFile(m_d->mountPoint), 0); + return true; } + m_d->mountTimer.stop(); + m_d->mountProc = new KProcess(this); m_d->mountProc->setOutputChannelMode(KProcess::SeparateChannels); @@ -106,6 +159,10 @@ bool SftpPlugin::receivePackage(const NetworkPackage& np) connect(m_d->mountProc, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(onFinished(int,QProcess::ExitStatus))); connect(m_d->mountProc, SIGNAL(finished(int,QProcess::ExitStatus)), m_d->mountProc, SLOT(deleteLater())); + const QString mpoint = KConfig("kdeconnect/plugins/sftp").group("main").readEntry("mountpoint" + , KStandardDirs::locateLocal("appdata", "", true, componentData())) + "/" + device()->name() + "/"; + QDir().mkpath(mpoint); + const QString program = "sshfs"; const QStringList arguments = QStringList() << QString("%1@%2:%3") @@ -116,29 +173,49 @@ bool SftpPlugin::receivePackage(const NetworkPackage& np) << "-d" << "-f" << "-o" << "password_stdin" - << m_d->mountPoint; + << mpoint; m_d->mountProc->setProgram(program, arguments); m_d->mountProc->setProperty(passwd_c, np.get("password")); + m_d->mountProc->setProperty(mountpoint_c, mpoint); - cleanMountPoint(); + cleanMountPoint(m_d->mountProc); m_d->mountProc->start(); return true; } +void SftpPlugin::timerEvent(QTimerEvent* event) +{ + if (event->timerId() == m_d->idleTimer) + { + if (isTimeout(m_d->mountProc, SftpConfig::config()->group("main"))) + { + umount(); + } + } + + QObject::timerEvent(event); +} + void SftpPlugin::onStarted() { + m_d->mountProc->setProperty(timestamp_c, QDateTime::currentDateTime()); + m_d->mountProc->write(m_d->mountProc->property(passwd_c).toString().toLocal8Bit() + "\n"); m_d->mountProc->closeWriteChannel(); knotify(KNotification::Notification - , i18n("Device %1").arg(device()->name()) - , i18n("Filesystem mounted at %1").arg(m_d->mountPoint) + , i18n("Filesystem mounted at %1").arg(mountpoint(sender())) , KIconLoader::global()->loadIcon("drive-removable-media", KIconLoader::Desktop) ); - new KRun(KUrl::fromLocalFile(m_d->mountPoint), 0); + if (m_d->waitForMount) + { + m_d->waitForMount = false; + new KRun(KUrl::fromLocalFile(mountpoint(sender())), 0); + } + } void SftpPlugin::onError(QProcess::ProcessError error) @@ -146,11 +223,10 @@ void SftpPlugin::onError(QProcess::ProcessError error) if (error == QProcess::FailedToStart) { knotify(KNotification::Error - , i18n("Device %1").arg(device()->name()) , i18n("Failed to start sshfs") , KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Desktop) ); - cleanMountPoint(); + cleanMountPoint(sender()); } } @@ -160,34 +236,57 @@ void SftpPlugin::onFinished(int exitCode, QProcess::ExitStatus exitStatus) if (exitStatus == QProcess::NormalExit) { - knotify(KNotification::Notification - , i18n("Device %1").arg(device()->name()) - , i18n("Filesystem unmounted") - , KIconLoader::global()->loadIcon("dialog-ok", KIconLoader::Desktop) - ); + if (isTimeout(sender(), SftpConfig::config()->group("main"))) + { + knotify(KNotification::Notification + , i18n("Filesystem unmounted by idle timeout") + , KIconLoader::global()->loadIcon("clock", KIconLoader::Desktop) + ); + } + else + { + knotify(KNotification::Notification + , i18n("Filesystem unmounted") + , KIconLoader::global()->loadIcon("dialog-ok", KIconLoader::Desktop) + ); + } } else { knotify(KNotification::Error - , i18n("Device %1").arg(device()->name()) , i18n("Error when accessing to filesystem") , KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Desktop) ); } - cleanMountPoint(); + cleanMountPoint(sender()); m_d->mountProc = 0; } -void SftpPlugin::knotify(int type, const QString& title, const QString& text, const QPixmap& icon) const +void SftpPlugin::knotify(int type, const QString& text, const QPixmap& icon) const { - KNotification::event(KNotification::StandardEvent(type), title, text, icon, 0 + KNotification::event(KNotification::StandardEvent(type) + , i18n("Device %1").arg(device()->name()), text, icon, 0 , KNotification::CloseOnTimeout); } -void SftpPlugin::cleanMountPoint() +void SftpPlugin::cleanMountPoint(QObject* mounter) { - if (m_d->mountProc.isNull()) return; + if (!mounter || mountpoint(mounter).isEmpty()) + { + return; + } - KProcess::execute(QStringList() << "fusermount" << "-u" << m_d->mountPoint, 10000); + KProcess::execute(QStringList() + << "fusermount" << "-u" + << mountpoint(mounter), 10000); } + +void SftpPlugin::mountTimeout() +{ + knotify(KNotification::Error + , i18n("Failed to mount filesystem: device not responding") + , KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Desktop) + ); +} + diff --git a/kded/plugins/sftp/sftpplugin.h b/kded/plugins/sftp/sftpplugin.h index a3100e47d..f5d3831f4 100644 --- a/kded/plugins/sftp/sftpplugin.h +++ b/kded/plugins/sftp/sftpplugin.h @@ -39,22 +39,33 @@ public: explicit SftpPlugin(QObject *parent, const QVariantList &args); virtual ~SftpPlugin(); + inline static KComponentData componentData() + { + return KComponentData("kdeconnect", "kdeconnect"); + } + public Q_SLOTS: virtual bool receivePackage(const NetworkPackage& np); virtual void connected(); + Q_SCRIPTABLE void mount(); + Q_SCRIPTABLE void umount(); + Q_SCRIPTABLE void startBrowsing(); - Q_SCRIPTABLE void stopBrowsing(); + +protected: + void timerEvent(QTimerEvent *event); private Q_SLOTS: void onStarted(); void onError(QProcess::ProcessError error); void onFinished(int exitCode, QProcess::ExitStatus exitStatus); + void mountTimeout(); private: QString dbusPath() const { return "/modules/kdeconnect/devices/" + device()->id() + "/sftp"; } - void knotify(int type, const QString& title, const QString& text, const QPixmap& icon) const; - void cleanMountPoint(); + void knotify(int type, const QString& text, const QPixmap& icon) const; + void cleanMountPoint(QObject* mounter); private: struct Pimpl;