[SCM] kdeconnect packaging branch, master, updated. debian/0.9g-1-1183-g9d69498
Maximiliano Curia
maxy at moszumanska.debian.org
Fri Oct 14 14:27:15 UTC 2016
Gitweb-URL: http://git.debian.org/?p=pkg-kde/kde-extras/kdeconnect.git;a=commitdiff;h=c90eebd
The following commit has been merged in the master branch:
commit c90eebd4898904cb2c95af2ccd46051686a12fb3
Author: Samoilenko Yuri <kinnalru at gmail.com>
Date: Thu Jan 16 02:07:58 2014 +0400
Idle timeout for SFTP plugin
---
kded/plugins/sftp/sftp_config.cpp | 63 +++++++-----
kded/plugins/sftp/sftp_config.h | 11 +-
kded/plugins/sftp/sftp_config.ui | 206 +++++++++++++++++++++++++++++++++-----
kded/plugins/sftp/sftpplugin.cpp | 161 +++++++++++++++++++++++------
kded/plugins/sftp/sftpplugin.h | 17 +++-
5 files changed, 369 insertions(+), 89 deletions(-)
diff --git a/kded/plugins/sftp/sftp_config.cpp b/kded/plugins/sftp/sftp_config.cpp
index ce526b2..5b1d34c 100644
--- a/kded/plugins/sftp/sftp_config.cpp
+++ b/kded/plugins/sftp/sftp_config.cpp
@@ -26,6 +26,7 @@
#include <KSharedConfig>
#include <KStandardDirs>
+#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 acab3a9..5ed9b26 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<Ui::SftpConfigUi> m_ui;
+ KSharedConfigPtr m_cfg;
};
diff --git a/kded/plugins/sftp/sftp_config.ui b/kded/plugins/sftp/sftp_config.ui
index 00e42bf..9b01fb7 100644
--- a/kded/plugins/sftp/sftp_config.ui
+++ b/kded/plugins/sftp/sftp_config.ui
@@ -9,41 +9,28 @@
<rect>
<x>0</x>
<y>0</y>
- <width>353</width>
- <height>68</height>
+ <width>303</width>
+ <height>155</height>
</rect>
</property>
<property name="windowTitle">
<string>Share plugin settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <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">
+ <item row="1" column="2">
+ <widget class="KUrlRequester" name="mountpoint">
+ <property name="acceptDrops">
<bool>false</bool>
</property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="label">
+ <property name="mode">
+ <set>KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly</set>
+ </property>
<property name="text">
<string/>
</property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
</widget>
</item>
- <item row="1" column="0" colspan="2">
+ <item row="4" column="1" colspan="3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -56,15 +43,182 @@
</property>
</spacer>
</item>
- <item row="0" column="2">
- <widget class="QPushButton" name="check">
+ <item row="2" column="1" colspan="2">
+ <widget class="QCheckBox" name="idle">
<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>
</widget>
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>KUrlRequester</class>
+ <extends>QFrame</extends>
+ <header>kurlrequester.h</header>
+ </customwidget>
+ </customwidgets>
<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>
diff --git a/kded/plugins/sftp/sftpplugin.cpp b/kded/plugins/sftp/sftpplugin.cpp
index 2400977..3cb9792 100644
--- a/kded/plugins/sftp/sftpplugin.cpp
+++ b/kded/plugins/sftp/sftpplugin.cpp
@@ -21,40 +21,66 @@
#include "sftpplugin.h"
#include <QDBusConnection>
+#include <QDir>
+#include <QTimerEvent>
+#include <KConfig>
+#include <KConfigGroup>
#include <KIconLoader>
#include <KLocalizedString>
#include <KNotification>
#include <KProcess>
#include <KRun>
#include <KStandardDirs>
+#include <kde_file.h>
+#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<QString> fields_c = QSet<QString>() <<
"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<KProcess> 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()
+{
+ if (m_d->mountProc)
+ {
+ 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()
{
- cleanMountPoint();
if (m_d->mountProc)
{
- m_d->mountProc->terminate();
- QTimer::singleShot(5000, m_d->mountProc, SLOT(kill()));
- m_d->mountProc->waitForFinished();
+ 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<QString>("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() + "
");
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 a3100e4..f5d3831 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;
--
kdeconnect packaging
More information about the pkg-kde-commits
mailing list