2014-04-01 02:12:31 +01:00
|
|
|
/*
|
2015-02-05 07:52:07 +00:00
|
|
|
* Copyright 2015 Aleix Pol Gonzalez <aleixpol@kde.org>
|
2014-04-01 02:12:31 +01:00
|
|
|
*
|
|
|
|
* 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) version 3 or any later version
|
|
|
|
* accepted by the membership of KDE e.V. (or its successor approved
|
|
|
|
* by the membership of KDE e.V.), which shall act as a proxy
|
|
|
|
* defined in Section 14 of version 3 of the license.
|
|
|
|
*
|
|
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2015-07-19 15:55:28 +01:00
|
|
|
#include <QCryptographicHash>
|
2015-09-11 12:35:16 +01:00
|
|
|
#include <QIODevice>
|
2014-06-14 14:22:40 +01:00
|
|
|
#include <QDBusMessage>
|
|
|
|
#include <QDBusConnection>
|
2015-03-10 04:59:36 +00:00
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QTextStream>
|
|
|
|
|
2014-09-22 01:26:06 +01:00
|
|
|
#include <KAboutData>
|
|
|
|
#include <KLocalizedString>
|
2014-04-01 02:12:31 +01:00
|
|
|
|
2015-03-10 04:59:36 +00:00
|
|
|
#include "interfaces/devicesmodel.h"
|
|
|
|
#include "interfaces/notificationsmodel.h"
|
|
|
|
#include "interfaces/dbusinterfaces.h"
|
|
|
|
#include "kdeconnect-version.h"
|
2015-02-05 07:52:07 +00:00
|
|
|
|
2014-04-01 02:12:31 +01:00
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
2014-11-28 16:42:53 +00:00
|
|
|
QCoreApplication app(argc, argv);
|
2015-02-05 07:52:07 +00:00
|
|
|
KAboutData about("kdeconnect-cli",
|
|
|
|
"kdeconnect-cli",
|
|
|
|
QLatin1String(KDECONNECT_VERSION_STRING),
|
|
|
|
i18n("KDE Connect CLI tool"),
|
|
|
|
KAboutLicense::GPL,
|
|
|
|
i18n("(C) 2015 Aleix Pol Gonzalez"));
|
2014-09-22 01:26:06 +01:00
|
|
|
KAboutData::setApplicationData(about);
|
2014-07-01 22:59:38 +01:00
|
|
|
|
2014-09-22 01:26:06 +01:00
|
|
|
about.addAuthor( i18n("Aleix Pol Gonzalez"), QString(), "aleixpol@kde.org" );
|
2014-11-12 05:40:54 +00:00
|
|
|
about.addAuthor( i18n("Albert Vaca Cintora"), QString(), "albertvaka@gmail.com" );
|
2014-09-22 01:26:06 +01:00
|
|
|
QCommandLineParser parser;
|
|
|
|
parser.addOption(QCommandLineOption(QStringList("l") << "list-devices", i18n("List all devices")));
|
2015-02-25 06:13:30 +00:00
|
|
|
parser.addOption(QCommandLineOption(QStringList("a") << "list-available", i18n("List available (paired and reachable) devices")));
|
|
|
|
parser.addOption(QCommandLineOption("id-only", i18n("Make --list-devices or --list-available print only the devices id, to ease scripting")));
|
2015-03-20 08:07:34 +00:00
|
|
|
parser.addOption(QCommandLineOption("refresh", i18n("Search for devices in the network and re-establish connections")));
|
2014-09-22 01:26:06 +01:00
|
|
|
parser.addOption(QCommandLineOption("pair", i18n("Request pairing to a said device")));
|
2015-09-11 16:51:50 +01:00
|
|
|
parser.addOption(QCommandLineOption("ring", i18n("Find the said device by ringing it.")));
|
2014-09-22 01:26:06 +01:00
|
|
|
parser.addOption(QCommandLineOption("unpair", i18n("Stop pairing to a said device")));
|
|
|
|
parser.addOption(QCommandLineOption("ping", i18n("Sends a ping to said device")));
|
2014-11-12 05:40:54 +00:00
|
|
|
parser.addOption(QCommandLineOption("ping-msg", i18n("Same as ping but you can set the message to display"), i18n("message")));
|
|
|
|
parser.addOption(QCommandLineOption("share", i18n("Share a file to a said device"), "path"));
|
2014-09-22 01:26:06 +01:00
|
|
|
parser.addOption(QCommandLineOption("list-notifications", i18n("Display the notifications on a said device")));
|
2015-07-22 02:37:34 +01:00
|
|
|
parser.addOption(QCommandLineOption("lock", i18n("Lock the specified device")));
|
2014-11-12 11:58:25 +00:00
|
|
|
parser.addOption(QCommandLineOption(QStringList("device") << "d", i18n("Device ID"), "dev"));
|
2015-07-18 14:24:10 +01:00
|
|
|
parser.addOption(QCommandLineOption("encryption-info", i18n("Get encryption info about said device")));
|
2014-09-22 01:26:06 +01:00
|
|
|
about.setupCommandLine(&parser);
|
|
|
|
|
|
|
|
parser.addHelpOption();
|
|
|
|
parser.process(app);
|
|
|
|
about.processCommandLine(&parser);
|
|
|
|
|
2015-09-12 21:07:05 +01:00
|
|
|
const QString id = "kdeconnect-cli-"+QString::number(QCoreApplication::applicationPid());
|
|
|
|
DaemonDbusInterface iface;
|
|
|
|
|
2015-02-25 06:13:30 +00:00
|
|
|
if(parser.isSet("l") || parser.isSet("a")) {
|
2015-03-16 01:58:22 +00:00
|
|
|
bool paired = true, reachable = false;
|
2015-02-25 06:13:30 +00:00
|
|
|
if (parser.isSet("a")) {
|
2015-03-16 01:58:22 +00:00
|
|
|
reachable = true;
|
2015-09-09 22:22:59 +01:00
|
|
|
} else {
|
|
|
|
iface.acquireDiscoveryMode(id);
|
|
|
|
QThread::sleep(2);
|
2015-02-25 06:13:30 +00:00
|
|
|
}
|
2015-03-16 01:58:22 +00:00
|
|
|
QDBusPendingReply<QStringList> reply = iface.devices(paired, reachable);
|
|
|
|
reply.waitForFinished();
|
|
|
|
|
|
|
|
const QStringList devices = reply.value();
|
|
|
|
foreach (const QString& id, devices) {
|
2015-02-25 06:13:30 +00:00
|
|
|
if (parser.isSet("id-only")) {
|
2015-03-16 01:58:22 +00:00
|
|
|
QTextStream(stdout) << id << endl;
|
2015-02-25 06:13:30 +00:00
|
|
|
} else {
|
2015-03-16 01:58:22 +00:00
|
|
|
DeviceDbusInterface deviceIface(id);
|
2015-02-25 06:13:30 +00:00
|
|
|
QString statusInfo;
|
2015-09-12 21:07:05 +01:00
|
|
|
const bool isReachable = deviceIface.isReachable(), isPaired = deviceIface.property("isPaired").toBool();
|
2015-03-16 01:58:22 +00:00
|
|
|
if (isReachable && isPaired) {
|
|
|
|
statusInfo = i18n("(paired and reachable)");
|
|
|
|
} else if (isReachable) {
|
|
|
|
statusInfo = i18n("(reachable)");
|
|
|
|
} else if (isPaired)
|
|
|
|
statusInfo = i18n("(paired)");
|
|
|
|
QTextStream(stdout) << "- " << deviceIface.name()
|
|
|
|
<< ": " << deviceIface.id() << ' ' << statusInfo << endl;
|
2014-06-27 15:18:43 +01:00
|
|
|
}
|
2014-04-01 02:12:31 +01:00
|
|
|
}
|
2015-02-25 06:13:30 +00:00
|
|
|
if (!parser.isSet("id-only")) {
|
2015-03-16 01:58:22 +00:00
|
|
|
QTextStream(stdout) << i18np("1 device found", "%1 devices found", devices.size()) << endl;
|
|
|
|
} else if (devices.isEmpty()) {
|
2015-02-25 06:13:30 +00:00
|
|
|
QTextStream(stderr) << i18n("No devices found") << endl;
|
|
|
|
}
|
2015-09-09 22:22:59 +01:00
|
|
|
|
|
|
|
iface.releaseDiscoveryMode(id);
|
2014-11-12 11:58:25 +00:00
|
|
|
} else if(parser.isSet("refresh")) {
|
2014-09-26 16:36:39 +01:00
|
|
|
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kdeconnect", "/modules/kdeconnect", "org.kde.kdeconnect.daemon", "forceOnNetworkChange");
|
|
|
|
QDBusConnection::sessionBus().call(msg);
|
2014-04-01 02:12:31 +01:00
|
|
|
} else {
|
|
|
|
QString device;
|
2014-09-22 01:26:06 +01:00
|
|
|
if(!parser.isSet("device")) {
|
2015-02-25 06:13:30 +00:00
|
|
|
QTextStream(stderr) << i18n("No device specified") << endl;
|
2014-04-01 02:12:31 +01:00
|
|
|
}
|
2014-09-22 01:26:06 +01:00
|
|
|
device = parser.value("device");
|
|
|
|
if(parser.isSet("share")) {
|
2015-09-11 11:22:35 +01:00
|
|
|
QUrl url = QUrl::fromUserInput(parser.value("share"), QDir::currentPath());
|
2014-09-22 01:26:06 +01:00
|
|
|
parser.clearPositionalArguments();
|
2014-06-23 17:15:10 +01:00
|
|
|
if(!url.isEmpty() && !device.isEmpty()) {
|
|
|
|
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kdeconnect", "/modules/kdeconnect/devices/"+device+"/share", "org.kde.kdeconnect.device.share", "shareUrl");
|
|
|
|
msg.setArguments(QVariantList() << url.toString());
|
|
|
|
QDBusConnection::sessionBus().call(msg);
|
2015-02-25 06:13:30 +00:00
|
|
|
} else {
|
|
|
|
QTextStream(stderr) << (i18n("Couldn't share %1", url.toString())) << endl;
|
|
|
|
}
|
2014-09-22 01:26:06 +01:00
|
|
|
} else if(parser.isSet("pair")) {
|
2014-06-23 17:15:10 +01:00
|
|
|
DeviceDbusInterface dev(device);
|
2015-09-12 21:07:05 +01:00
|
|
|
if (!dev.isReachable()) {
|
|
|
|
//Device doesn't exist, go into discovery mode and wait up to 30 seconds for the device to appear
|
|
|
|
QEventLoop wait;
|
|
|
|
QTextStream(stderr) << i18n("waiting for device...") << endl;
|
|
|
|
iface.acquireDiscoveryMode(id);
|
|
|
|
|
|
|
|
QObject::connect(&iface, &DaemonDbusInterface::deviceAdded, [&](const QString &deviceAddedId) {
|
|
|
|
if (device == deviceAddedId) {
|
|
|
|
wait.quit();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
QTimer::singleShot(30 * 1000, &wait, &QEventLoop::quit);
|
|
|
|
|
|
|
|
wait.exec();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev.isReachable()) {
|
|
|
|
QTextStream(stderr) << i18n("Device not found") << endl;
|
|
|
|
} else if(dev.property("isPaired").toBool()) {
|
2015-02-25 06:13:30 +00:00
|
|
|
QTextStream(stderr) << i18n("Already paired") << endl;
|
2015-09-12 21:07:05 +01:00
|
|
|
} else {
|
|
|
|
QTextStream(stderr) << i18n("Pair requested") << endl;
|
2014-06-23 17:15:10 +01:00
|
|
|
QDBusPendingReply<void> req = dev.requestPair();
|
|
|
|
req.waitForFinished();
|
|
|
|
}
|
2015-09-12 21:07:05 +01:00
|
|
|
iface.releaseDiscoveryMode(id);
|
2014-09-22 01:26:06 +01:00
|
|
|
} else if(parser.isSet("unpair")) {
|
2014-06-23 17:15:10 +01:00
|
|
|
DeviceDbusInterface dev(device);
|
2015-09-12 21:07:05 +01:00
|
|
|
if (!dev.isReachable()) {
|
|
|
|
QTextStream(stderr) << i18n("Device does not exist") << endl;
|
|
|
|
} else if(!dev.property("isPaired").toBool()) {
|
2015-02-25 06:13:30 +00:00
|
|
|
QTextStream(stderr) << i18n("Already not paired") << endl;
|
2015-09-12 21:07:05 +01:00
|
|
|
} else {
|
|
|
|
QTextStream(stderr) << i18n("Unpaired") << endl;
|
2014-06-23 17:15:10 +01:00
|
|
|
QDBusPendingReply<void> req = dev.unpair();
|
|
|
|
req.waitForFinished();
|
|
|
|
}
|
2014-11-04 18:12:29 +00:00
|
|
|
} else if(parser.isSet("ping") || parser.isSet("ping-msg")) {
|
2014-06-27 17:21:40 +01:00
|
|
|
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kdeconnect", "/modules/kdeconnect/devices/"+device+"/ping", "org.kde.kdeconnect.device.ping", "sendPing");
|
2014-11-04 18:12:29 +00:00
|
|
|
if (parser.isSet("ping-msg")) {
|
|
|
|
QString message = parser.value("ping-msg");
|
2014-09-27 04:39:38 +01:00
|
|
|
msg.setArguments(QVariantList() << message);
|
|
|
|
}
|
2014-04-01 02:12:31 +01:00
|
|
|
QDBusConnection::sessionBus().call(msg);
|
2015-09-11 16:51:50 +01:00
|
|
|
} else if(parser.isSet("ring")) {
|
|
|
|
QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kdeconnect", "/modules/kdeconnect/devices/"+device+"/findmyphone", "org.kde.kdeconnect.device.findmyphone", "ring");
|
|
|
|
QDBusConnection::sessionBus().call(msg);
|
2014-09-22 01:26:06 +01:00
|
|
|
} else if(parser.isSet("list-notifications")) {
|
2014-07-01 00:15:23 +01:00
|
|
|
NotificationsModel notifications;
|
|
|
|
notifications.setDeviceId(device);
|
|
|
|
for(int i=0, rows=notifications.rowCount(); i<rows; ++i) {
|
|
|
|
QModelIndex idx = notifications.index(i);
|
2014-11-10 01:14:53 +00:00
|
|
|
QTextStream(stdout) << "- " << idx.data(NotificationsModel::AppNameModelRole).toString()
|
2015-02-25 06:13:30 +00:00
|
|
|
<< ": " << idx.data(NotificationsModel::NameModelRole).toString() << endl;
|
2014-07-01 00:15:23 +01:00
|
|
|
}
|
2015-07-18 14:24:10 +01:00
|
|
|
} else if(parser.isSet("encryption-info")) {
|
2015-09-11 16:25:23 +01:00
|
|
|
DeviceDbusInterface dev(device);
|
|
|
|
QDBusPendingReply<QByteArray> devReply = dev.certificate(1); // QSsl::Der = 1
|
|
|
|
devReply.waitForFinished();
|
|
|
|
if (devReply.value().isEmpty()) {
|
|
|
|
QTextStream(stderr) << i18n("The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.") << endl;
|
|
|
|
} else {
|
|
|
|
QByteArray remoteCertificate = QCryptographicHash::hash(devReply.value(), QCryptographicHash::Sha1).toHex();
|
|
|
|
for (int i=2 ; i<remoteCertificate.size() ; i+=3)
|
|
|
|
remoteCertificate.insert(i, ':'); // Improve readability
|
2015-07-19 15:55:28 +01:00
|
|
|
|
2015-09-11 16:25:23 +01:00
|
|
|
DaemonDbusInterface iface;
|
|
|
|
QDBusPendingReply<QByteArray> ifaceReply = iface.certificate(1); // QSsl::Der = 1
|
|
|
|
ifaceReply.waitForFinished();
|
|
|
|
QByteArray myCertificate = QCryptographicHash::hash(ifaceReply.value(), QCryptographicHash::Sha1).toHex();
|
|
|
|
for (int i=2 ; i<myCertificate.size() ; i+=3)
|
|
|
|
myCertificate.insert(i, ':'); // Improve readability
|
2015-07-19 15:55:28 +01:00
|
|
|
|
2015-09-11 16:25:23 +01:00
|
|
|
QTextStream(stderr) << i18n("SHA1 fingerprint of your device certificate is : ") << myCertificate << endl;
|
|
|
|
QTextStream(stderr) << i18n("SHA1 fingerprint of remote device certificate is : ") << remoteCertificate << endl;
|
|
|
|
}
|
2014-07-01 00:15:23 +01:00
|
|
|
} else {
|
2015-02-25 06:13:30 +00:00
|
|
|
QTextStream(stderr) << i18n("Nothing to be done") << endl;
|
2014-07-01 00:15:23 +01:00
|
|
|
}
|
2014-04-01 02:12:31 +01:00
|
|
|
}
|
|
|
|
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
|
|
|
|
|
|
|
|
return app.exec();
|
|
|
|
}
|