Idle timeout for SFTP plugin

This commit is contained in:
Samoilenko Yuri 2014-01-16 02:07:58 +04:00
parent 9fc9e72374
commit c90eebd489
5 changed files with 369 additions and 89 deletions

View file

@ -26,6 +26,7 @@
#include <KSharedConfig> #include <KSharedConfig>
#include <KStandardDirs> #include <KStandardDirs>
#include "sftpplugin.h"
#include "../../kdebugnamespace.h" #include "../../kdebugnamespace.h"
#include "ui_sftp_config.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& ) SftpConfig::SftpConfig(QWidget *parent, const QVariantList& )
: KCModule(SftpConfigFactory::componentData(), parent) : KCModule(SftpConfigFactory::componentData(), parent)
, ui(new Ui::SftpConfigUi()) , m_ui(new Ui::SftpConfigUi())
, cfg(KSharedConfig::openConfig("kdeconnect/plugins/sftp")) , m_cfg(SftpConfig::config())
{ {
ui->setupUi(this); m_ui->setupUi(this);
ui->check->setIcon(KIconLoader::global()->loadIcon("view-refresh", KIconLoader::Dialog)); m_ui->refresh->setIcon(KIconLoader::global()->loadIcon("view-refresh", KIconLoader::Dialog));
connect(ui->check, SIGNAL(clicked(bool)), this, SLOT(refresh())); m_ui->pixmap->setPixmap(KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Dialog));
refresh();
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() SftpConfig::~SftpConfig()
{ {
delete ui;
} }
void SftpConfig::refresh() void SftpConfig::checkSshfs()
{ {
const QString sshfs = KStandardDirs::findExe("sshfs"); m_ui->error->setVisible(KStandardDirs::findExe("sshfs").isEmpty());
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));
}
} }
void SftpConfig::defaults() void SftpConfig::defaults()
{ {
KCModule::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() void SftpConfig::load()
{ {
KCModule::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() 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(); KCModule::save();
//Q_EMIT changed(false); Q_EMIT changed(false);
} }

View file

@ -36,17 +36,22 @@ public:
SftpConfig(QWidget *parent, const QVariantList&); SftpConfig(QWidget *parent, const QVariantList&);
virtual ~SftpConfig(); virtual ~SftpConfig();
static inline KSharedConfigPtr config()
{
return KSharedConfig::openConfig("kdeconnect/plugins/sftp");
}
public Q_SLOTS: public Q_SLOTS:
virtual void save(); virtual void save();
virtual void load(); virtual void load();
virtual void defaults(); virtual void defaults();
private Q_SLOTS: private Q_SLOTS:
void refresh(); void checkSshfs();
private: private:
Ui::SftpConfigUi* ui; QScopedPointer<Ui::SftpConfigUi> m_ui;
KSharedConfigPtr cfg; KSharedConfigPtr m_cfg;
}; };

View file

@ -9,41 +9,28 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>353</width> <width>303</width>
<height>68</height> <height>155</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Share plugin settings</string> <string>Share plugin settings</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="1" column="2">
<widget class="QLabel" name="pixmap"> <widget class="KUrlRequester" name="mountpoint">
<property name="sizePolicy"> <property name="acceptDrops">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> <property name="mode">
</item> <set>KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly</set>
<item row="0" column="1"> </property>
<widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="4" column="1" colspan="3">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -56,15 +43,182 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="0" column="2"> <item row="2" column="1" colspan="2">
<widget class="QPushButton" name="check"> <widget class="QCheckBox" name="idle">
<property name="text"> <property name="text">
<string/> <string>Disconnect when idle</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QGroupBox" name="error">
<property name="title">
<string>Error</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="pixmap">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>sshfsf not found in PATH</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refresh">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Mountpoint:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>16</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Timeout:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="timeout">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> min</string>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>99999999</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>53</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>...</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>KUrlRequester</class>
<extends>QFrame</extends>
<header>kurlrequester.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections>
<connection>
<sender>idle</sender>
<signal>toggled(bool)</signal>
<receiver>frame</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>98</y>
</hint>
<hint type="destinationlabel">
<x>14</x>
<y>127</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View file

@ -21,40 +21,66 @@
#include "sftpplugin.h" #include "sftpplugin.h"
#include <QDBusConnection> #include <QDBusConnection>
#include <QDir>
#include <QTimerEvent>
#include <KConfig>
#include <KConfigGroup>
#include <KIconLoader> #include <KIconLoader>
#include <KLocalizedString> #include <KLocalizedString>
#include <KNotification> #include <KNotification>
#include <KProcess> #include <KProcess>
#include <KRun> #include <KRun>
#include <KStandardDirs> #include <KStandardDirs>
#include <kde_file.h>
#include "sftp_config.h"
#include "../../kdebugnamespace.h" #include "../../kdebugnamespace.h"
K_PLUGIN_FACTORY( KdeConnectPluginFactory, registerPlugin< SftpPlugin >(); ) K_PLUGIN_FACTORY( KdeConnectPluginFactory, registerPlugin< SftpPlugin >(); )
K_EXPORT_PLUGIN( KdeConnectPluginFactory("kdeconnect_sftp", "kdeconnect_sftp") ) K_EXPORT_PLUGIN( KdeConnectPluginFactory("kdeconnect_sftp", "kdeconnect_sftp") )
static const char* passwd_c = "sftppassword"; static const char* passwd_c = "sftppassword";
static const char* mountpoint_c = "sftpmountpoint";
static const char* timestamp_c = "timestamp";
static const QSet<QString> fields_c = QSet<QString>() << static const QSet<QString> fields_c = QSet<QString>() <<
"ip" << "port" << "user" << "port" << "password" << "path"; "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 struct SftpPlugin::Pimpl
{ {
Pimpl() {}; Pimpl() : waitForMount(false)
{
mountTimer.setSingleShot(true);
};
QString mountPoint;
QPointer<KProcess> mountProc; QPointer<KProcess> mountProc;
QTimer mountTimer;
int idleTimer;
bool waitForMount;
}; };
SftpPlugin::SftpPlugin(QObject *parent, const QVariantList &args) SftpPlugin::SftpPlugin(QObject *parent, const QVariantList &args)
: KdeConnectPlugin(parent, args) : KdeConnectPlugin(parent, args)
, m_d(new Pimpl) , m_d(new Pimpl)
{ {
m_d->mountPoint = KStandardDirs::locateLocal("appdata", device()->name() + "/", true,
KComponentData("kdeconnect", "kdeconnect"));
QDBusConnection::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents); 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() void SftpPlugin::connected()
@ -64,24 +90,49 @@ void SftpPlugin::connected()
SftpPlugin::~SftpPlugin() SftpPlugin::~SftpPlugin()
{ {
QDBusConnection::sessionBus().unregisterObject(dbusPath(), QDBusConnection::UnregisterTree); 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); NetworkPackage np(PACKAGE_TYPE_SFTP);
np.set("startBrowsing", true); np.set("startBrowsing", true);
device()->sendPackage(np); device()->sendPackage(np);
} }
void SftpPlugin::stopBrowsing() void SftpPlugin::umount()
{ {
cleanMountPoint();
if (m_d->mountProc) if (m_d->mountProc)
{ {
m_d->mountProc->terminate(); cleanMountPoint(m_d->mountProc);
QTimer::singleShot(5000, m_d->mountProc, SLOT(kill())); if (m_d->mountProc)
m_d->mountProc->waitForFinished(); {
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()) 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 = new KProcess(this);
m_d->mountProc->setOutputChannelMode(KProcess::SeparateChannels); 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)), SLOT(onFinished(int,QProcess::ExitStatus)));
connect(m_d->mountProc, SIGNAL(finished(int,QProcess::ExitStatus)), m_d->mountProc, SLOT(deleteLater())); 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 QString program = "sshfs";
const QStringList arguments = QStringList() const QStringList arguments = QStringList()
<< QString("%1@%2:%3") << QString("%1@%2:%3")
@ -116,29 +173,49 @@ bool SftpPlugin::receivePackage(const NetworkPackage& np)
<< "-d" << "-d"
<< "-f" << "-f"
<< "-o" << "password_stdin" << "-o" << "password_stdin"
<< m_d->mountPoint; << mpoint;
m_d->mountProc->setProgram(program, arguments); m_d->mountProc->setProgram(program, arguments);
m_d->mountProc->setProperty(passwd_c, np.get<QString>("password")); m_d->mountProc->setProperty(passwd_c, np.get<QString>("password"));
m_d->mountProc->setProperty(mountpoint_c, mpoint);
cleanMountPoint(); cleanMountPoint(m_d->mountProc);
m_d->mountProc->start(); m_d->mountProc->start();
return true; 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() 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->write(m_d->mountProc->property(passwd_c).toString().toLocal8Bit() + "\n");
m_d->mountProc->closeWriteChannel(); m_d->mountProc->closeWriteChannel();
knotify(KNotification::Notification knotify(KNotification::Notification
, i18n("Device %1").arg(device()->name()) , i18n("Filesystem mounted at %1").arg(mountpoint(sender()))
, i18n("Filesystem mounted at %1").arg(m_d->mountPoint)
, KIconLoader::global()->loadIcon("drive-removable-media", KIconLoader::Desktop) , 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) void SftpPlugin::onError(QProcess::ProcessError error)
@ -146,11 +223,10 @@ void SftpPlugin::onError(QProcess::ProcessError error)
if (error == QProcess::FailedToStart) if (error == QProcess::FailedToStart)
{ {
knotify(KNotification::Error knotify(KNotification::Error
, i18n("Device %1").arg(device()->name())
, i18n("Failed to start sshfs") , i18n("Failed to start sshfs")
, KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Desktop) , 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) if (exitStatus == QProcess::NormalExit)
{ {
knotify(KNotification::Notification if (isTimeout(sender(), SftpConfig::config()->group("main")))
, i18n("Device %1").arg(device()->name()) {
, i18n("Filesystem unmounted") knotify(KNotification::Notification
, KIconLoader::global()->loadIcon("dialog-ok", KIconLoader::Desktop) , 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 else
{ {
knotify(KNotification::Error knotify(KNotification::Error
, i18n("Device %1").arg(device()->name())
, i18n("Error when accessing to filesystem") , i18n("Error when accessing to filesystem")
, KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Desktop) , KIconLoader::global()->loadIcon("dialog-error", KIconLoader::Desktop)
); );
} }
cleanMountPoint(); cleanMountPoint(sender());
m_d->mountProc = 0; 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); , 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)
);
}

View file

@ -39,22 +39,33 @@ public:
explicit SftpPlugin(QObject *parent, const QVariantList &args); explicit SftpPlugin(QObject *parent, const QVariantList &args);
virtual ~SftpPlugin(); virtual ~SftpPlugin();
inline static KComponentData componentData()
{
return KComponentData("kdeconnect", "kdeconnect");
}
public Q_SLOTS: public Q_SLOTS:
virtual bool receivePackage(const NetworkPackage& np); virtual bool receivePackage(const NetworkPackage& np);
virtual void connected(); virtual void connected();
Q_SCRIPTABLE void mount();
Q_SCRIPTABLE void umount();
Q_SCRIPTABLE void startBrowsing(); Q_SCRIPTABLE void startBrowsing();
Q_SCRIPTABLE void stopBrowsing();
protected:
void timerEvent(QTimerEvent *event);
private Q_SLOTS: private Q_SLOTS:
void onStarted(); void onStarted();
void onError(QProcess::ProcessError error); void onError(QProcess::ProcessError error);
void onFinished(int exitCode, QProcess::ExitStatus exitStatus); void onFinished(int exitCode, QProcess::ExitStatus exitStatus);
void mountTimeout();
private: private:
QString dbusPath() const { return "/modules/kdeconnect/devices/" + device()->id() + "/sftp"; } 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 knotify(int type, const QString& text, const QPixmap& icon) const;
void cleanMountPoint(); void cleanMountPoint(QObject* mounter);
private: private:
struct Pimpl; struct Pimpl;