diff --git a/CMakeLists.txt b/CMakeLists.txt index d89581d7..b82da6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,136 +1,136 @@ cmake_minimum_required(VERSION 3.0) project(kdeconnect) set(KDECONNECT_VERSION_MAJOR 1) set(KDECONNECT_VERSION_MINOR 3) set(KDECONNECT_VERSION_PATCH 3) set(KDECONNECT_VERSION "${KDECONNECT_VERSION_MAJOR}.${KDECONNECT_VERSION_MINOR}.${KDECONNECT_VERSION_PATCH}") find_package(PkgConfig) if (SAILFISHOS) set(KF5_MIN_VERSION "5.31.0") set(QT_MIN_VERSION "5.6.0") set(KF5_REQUIRED_COMPONENTS I18n DBusAddons CoreAddons IconThemes Config) set(KF5_OPTIONAL_COMPONENTS) set(QCA_MIN_VERSION 2.0.0) pkg_search_module(SFOS REQUIRED sailfishapp) pkg_check_modules(QCA2 qca2-qt5>=${QCA_MIN_VERSION} REQUIRED) add_definitions(-DSAILFISHOS) include_directories(${QCA2_INCLUDEDIR}) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) else() set(KF5_MIN_VERSION "5.42.0") set(QT_MIN_VERSION "5.10.0") set(KF5_REQUIRED_COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils Service) set(KF5_OPTIONAL_COMPONENTS DocTools) if(UNIX) set(KF5_OPTIONAL_COMPONENTS ${KF5_OPTIONAL_COMPONENTS} Runner) endif() set(QCA_MIN_VERSION "2.1.0") find_package(Qca-qt5 ${QCA_MIN_VERSION} REQUIRED) if(NOT WIN32 AND NOT APPLE) find_package(KF5PulseAudioQt REQUIRED) endif() - add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS) + add_definitions(-DQT_NO_URL_CAST_FROM_STRING -DQT_NO_KEYWORDS -DQT_NO_CAST_FROM_ASCII) endif() find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Quick Network) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS ${KF5_REQUIRED_COMPONENTS}) if (KF5_OPTIONAL_COMPONENTS) find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS ${KF5_OPTIONAL_COMPONENTS}) endif() if (NOT ZSH_AUTOCOMPLETE_DIR) set(ZSH_AUTOCOMPLETE_DIR "${CMAKE_INSTALL_PREFIX}/share/zsh/site-functions") endif() find_package(Qt5Multimedia) set_package_properties(KF5Kirigami2 PROPERTIES DESCRIPTION "QtQuick plugins to build user interfaces based on KDE UX guidelines" PURPOSE "Required for KDE Connect's QML-based GUI applications" URL "https://www.kde.org/products/kirigami/" TYPE RUNTIME ) configure_file(kdeconnect-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/kdeconnect-version.h) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMAddTests) include(ECMSetupVersion) include(ECMInstallIcons) include(FeatureSummary) include(KDEConnectMacros.cmake) include(GenerateExportHeader) add_subdirectory(core) if(NOT SAILFISHOS) add_subdirectory(kcm) add_subdirectory(kcmplugin) add_subdirectory(daemon) endif() if(NOT WIN32 AND NOT SAILFISHOS) add_subdirectory(kio) add_subdirectory(plasmoid) endif() add_subdirectory(icon) add_subdirectory(interfaces) add_subdirectory(data) option(EXPERIMENTALAPP_ENABLED OFF) if(EXPERIMENTALAPP_ENABLED) find_package(KF5Kirigami2) add_subdirectory(app) endif() add_subdirectory(plugins) add_subdirectory(cli) add_subdirectory(declarativeplugin) if(KF5Runner_FOUND) add_subdirectory(runners) endif() if (NOT SAILFISHOS) add_subdirectory(indicator) add_subdirectory(urlhandler) add_subdirectory(nautilus-extension) add_subdirectory(fileitemactionplugin) else() add_subdirectory(sfos) endif() option(SMSAPP_ENABLED OFF) if(SMSAPP_ENABLED) find_package(KF5Kirigami2) find_package(KF5People REQUIRED) find_package(KF5PeopleVCard) set_package_properties(KF5PeopleVCard PROPERTIES PURPOSE "Read vcards from the file system" URL "https://phabricator.kde.org/source/kpeoplevcard/" TYPE RUNTIME ) add_subdirectory(smsapp) endif() if(KF5DocTools_FOUND) add_subdirectory(doc) endif() if(BUILD_TESTING AND NOT SAILFISHOS) add_subdirectory(tests) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/cli/kdeconnect-cli.cpp b/cli/kdeconnect-cli.cpp index 7c44ca49..5cfa88c4 100644 --- a/cli/kdeconnect-cli.cpp +++ b/cli/kdeconnect-cli.cpp @@ -1,317 +1,317 @@ /* * Copyright 2015 Aleix Pol Gonzalez * * 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 . */ #include #include #include #include #include #include #include #include "interfaces/devicesmodel.h" #include "interfaces/notificationsmodel.h" #include "interfaces/dbusinterfaces.h" #include "interfaces/dbushelpers.h" #include "kdeconnect-version.h" #include int main(int argc, char** argv) { QCoreApplication app(argc, argv); KAboutData about(QStringLiteral("kdeconnect-cli"), QStringLiteral("kdeconnect-cli"), QStringLiteral(KDECONNECT_VERSION_STRING), i18n("KDE Connect CLI tool"), KAboutLicense::GPL, i18n("(C) 2015 Aleix Pol Gonzalez")); KAboutData::setApplicationData(about); about.addAuthor( i18n("Aleix Pol Gonzalez"), QString(), QStringLiteral("aleixpol@kde.org") ); about.addAuthor( i18n("Albert Vaca Cintora"), QString(), QStringLiteral("albertvaka@gmail.com") ); QCommandLineParser parser; parser.addOption(QCommandLineOption(QStringList(QStringLiteral("l")) << QStringLiteral("list-devices"), i18n("List all devices"))); parser.addOption(QCommandLineOption(QStringList(QStringLiteral("a")) << QStringLiteral("list-available"), i18n("List available (paired and reachable) devices"))); parser.addOption(QCommandLineOption(QStringLiteral("id-only"), i18n("Make --list-devices or --list-available print only the devices id, to ease scripting"))); parser.addOption(QCommandLineOption(QStringLiteral("name-only"), i18n("Make --list-devices or --list-available print only the devices name, to ease scripting"))); parser.addOption(QCommandLineOption(QStringLiteral("id-name-only"), i18n("Make --list-devices or --list-available print only the devices id and name, to ease scripting"))); parser.addOption(QCommandLineOption(QStringLiteral("refresh"), i18n("Search for devices in the network and re-establish connections"))); parser.addOption(QCommandLineOption(QStringLiteral("pair"), i18n("Request pairing to a said device"))); parser.addOption(QCommandLineOption(QStringLiteral("ring"), i18n("Find the said device by ringing it."))); parser.addOption(QCommandLineOption(QStringLiteral("unpair"), i18n("Stop pairing to a said device"))); parser.addOption(QCommandLineOption(QStringLiteral("ping"), i18n("Sends a ping to said device"))); parser.addOption(QCommandLineOption(QStringLiteral("ping-msg"), i18n("Same as ping but you can set the message to display"), i18n("message"))); parser.addOption(QCommandLineOption(QStringLiteral("share"), i18n("Share a file to a said device"), QStringLiteral("path"))); parser.addOption(QCommandLineOption(QStringLiteral("share-text"), i18n("Share text to a said device"), QStringLiteral("text"))); parser.addOption(QCommandLineOption(QStringLiteral("list-notifications"), i18n("Display the notifications on a said device"))); parser.addOption(QCommandLineOption(QStringLiteral("lock"), i18n("Lock the specified device"))); parser.addOption(QCommandLineOption(QStringLiteral("send-sms"), i18n("Sends an SMS. Requires destination"), i18n("message"))); parser.addOption(QCommandLineOption(QStringLiteral("destination"), i18n("Phone number to send the message"), i18n("phone number"))); parser.addOption(QCommandLineOption(QStringList(QStringLiteral("device")) << QStringLiteral("d"), i18n("Device ID"), QStringLiteral("dev"))); parser.addOption(QCommandLineOption(QStringList(QStringLiteral("name")) << QStringLiteral("n"), i18n("Device Name"), QStringLiteral("name"))); parser.addOption(QCommandLineOption(QStringLiteral("encryption-info"), i18n("Get encryption info about said device"))); parser.addOption(QCommandLineOption(QStringLiteral("list-commands"), i18n("Lists remote commands and their ids"))); parser.addOption(QCommandLineOption(QStringLiteral("execute-command"), i18n("Executes a remote command by id"), QStringLiteral("id"))); parser.addOption(QCommandLineOption(QStringList{QStringLiteral("k"), QStringLiteral("send-keys")}, i18n("Sends keys to a said device"), QStringLiteral("key"))); parser.addOption(QCommandLineOption(QStringLiteral("my-id"), i18n("Display this device's id and exit"))); parser.addOption(QCommandLineOption(QStringLiteral("photo"), i18n("Open the connected device's camera and transfer the photo"))); //Hidden because it's an implementation detail QCommandLineOption deviceAutocomplete(QStringLiteral("shell-device-autocompletion")); deviceAutocomplete.setHidden(true); - deviceAutocomplete.setDescription("Outputs all available devices id's with their name and paired status"); //Not visible, so no translation needed - deviceAutocomplete.setValueName("shell"); + deviceAutocomplete.setDescription(QStringLiteral("Outputs all available devices id's with their name and paired status")); //Not visible, so no translation needed + deviceAutocomplete.setValueName(QStringLiteral("shell")); parser.addOption(deviceAutocomplete); about.setupCommandLine(&parser); parser.addHelpOption(); parser.process(app); about.processCommandLine(&parser); - const QString id = "kdeconnect-cli-"+QString::number(QCoreApplication::applicationPid()); + const QString id = QStringLiteral("kdeconnect-cli-") + QString::number(QCoreApplication::applicationPid()); DaemonDbusInterface iface; if (parser.isSet(QStringLiteral("my-id"))) { QTextStream(stdout) << iface.selfId() << endl; } else if (parser.isSet(QStringLiteral("l")) || parser.isSet(QStringLiteral("a"))) { bool reachable = false; if (parser.isSet(QStringLiteral("a"))) { reachable = true; } else { blockOnReply(iface.acquireDiscoveryMode(id)); QThread::sleep(2); } const QStringList devices = blockOnReply(iface.devices(reachable, false)); bool displayCount = true; for (const QString& id : devices) { if (parser.isSet(QStringLiteral("id-only"))) { QTextStream(stdout) << id << endl; displayCount = false; } else if (parser.isSet(QStringLiteral("name-only"))) { DeviceDbusInterface deviceIface(id); QTextStream(stdout) << deviceIface.name() << endl; displayCount = false; } else if (parser.isSet(QStringLiteral("id-name-only"))) { DeviceDbusInterface deviceIface(id); QTextStream(stdout) << id << ' ' << deviceIface.name() << endl; displayCount = false; } else { DeviceDbusInterface deviceIface(id); QString statusInfo; const bool isReachable = deviceIface.isReachable(); const bool isTrusted = deviceIface.isTrusted(); if (isReachable && isTrusted) { statusInfo = i18n("(paired and reachable)"); } else if (isReachable) { statusInfo = i18n("(reachable)"); } else if (isTrusted) { statusInfo = i18n("(paired)"); } QTextStream(stdout) << "- " << deviceIface.name() << ": " << deviceIface.id() << ' ' << statusInfo << endl; } } if (displayCount) { QTextStream(stderr) << i18np("1 device found", "%1 devices found", devices.size()) << endl; } else if (devices.isEmpty()) { QTextStream(stderr) << i18n("No devices found") << endl; } blockOnReply(iface.releaseDiscoveryMode(id)); } else if (parser.isSet(QStringLiteral("shell-device-autocompletion"))) { //Outputs a list of reachable devices in zsh autocomplete format, with the name as description const QStringList devices = blockOnReply(iface.devices(true, false)); for (const QString &id : devices) { DeviceDbusInterface deviceIface(id); QString statusInfo; const bool isTrusted = deviceIface.isTrusted(); if (isTrusted) { statusInfo = i18n("(paired)"); } else { statusInfo = i18n("(unpaired)"); } //Description: "device name (paired/unpaired)" - QString description = deviceIface.name() + " " + statusInfo; + QString description = deviceIface.name() + QLatin1Char(' ') + statusInfo; //Replace characters - description.replace(QChar('\\'), QStringLiteral("\\\\")); - description.replace(QChar('['), QStringLiteral("\\[")); - description.replace(QChar(']'), QStringLiteral("\\]")); - description.replace(QChar('\''), QStringLiteral("\\'")); - description.replace(QChar('\"'), QStringLiteral("\\\"")); - description.replace(QChar('\n'), QChar(' ')); - description.remove(QChar('\0')); + description.replace(QLatin1Char('\\'), QStringLiteral("\\\\")); + description.replace(QLatin1Char('['), QStringLiteral("\\[")); + description.replace(QLatin1Char(']'), QStringLiteral("\\]")); + description.replace(QLatin1Char('\''), QStringLiteral("\\'")); + description.replace(QLatin1Char('\"'), QStringLiteral("\\\"")); + description.replace(QLatin1Char('\n'), QLatin1Char(' ')); + description.remove(QLatin1Char('\0')); //Output id and description QTextStream(stdout) << id << '[' << description << ']' << endl; } } else if(parser.isSet(QStringLiteral("refresh"))) { QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect"), QStringLiteral("org.kde.kdeconnect.daemon"), QStringLiteral("forceOnNetworkChange")); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); } else { QString device = parser.value(QStringLiteral("device")); if (device.isEmpty() && parser.isSet(QStringLiteral("name"))) { device = blockOnReply(iface.deviceIdByName(parser.value(QStringLiteral("name")))); if (device.isEmpty()) { QTextStream(stderr) << "Couldn't find device: " << parser.value(QStringLiteral("name")) << endl; return 1; } } if(device.isEmpty()) { QTextStream(stderr) << i18n("No device specified") << endl; parser.showHelp(1); } if (parser.isSet(QStringLiteral("share"))) { QStringList urls; QUrl url = QUrl::fromUserInput(parser.value(QStringLiteral("share")), QDir::currentPath()); urls.append(url.toString()); // Check for more arguments const auto args = parser.positionalArguments(); for (const QString& input : args) { QUrl url = QUrl::fromUserInput(input, QDir::currentPath()); urls.append(url.toString()); } - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/share", + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/share"), QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareUrls")); msg.setArguments(QVariantList() << QVariant(urls)); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); for (const QString& url : qAsConst(urls)) { QTextStream(stdout) << i18n("Shared %1", url) << endl; } } else if (parser.isSet(QStringLiteral("share-text"))) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/share", QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareText")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/share"), QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareText")); msg.setArguments(QVariantList() << parser.value(QStringLiteral("share-text"))); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); QTextStream(stdout) << i18n("Shared text: %1", parser.value(QStringLiteral("share-text"))) << endl; } else if(parser.isSet(QStringLiteral("pair"))) { DeviceDbusInterface dev(device); 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; blockOnReply(iface.acquireDiscoveryMode(id)); QObject::connect(&iface, &DaemonDbusInterface::deviceAdded, &iface, [&](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(blockOnReply(dev.isTrusted())) { QTextStream(stderr) << i18n("Already paired") << endl; } else { QTextStream(stderr) << i18n("Pair requested") << endl; blockOnReply(dev.requestPair()); } blockOnReply(iface.releaseDiscoveryMode(id)); } else if(parser.isSet(QStringLiteral("unpair"))) { DeviceDbusInterface dev(device); if (!dev.isTrusted()) { QTextStream(stderr) << i18n("Already not paired") << endl; } else { QTextStream(stderr) << i18n("Unpaired") << endl; blockOnReply(dev.unpair()); } } else if(parser.isSet(QStringLiteral("ping")) || parser.isSet(QStringLiteral("ping-msg"))) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/ping", QStringLiteral("org.kde.kdeconnect.device.ping"), QStringLiteral("sendPing")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/ping"), QStringLiteral("org.kde.kdeconnect.device.ping"), QStringLiteral("sendPing")); if (parser.isSet(QStringLiteral("ping-msg"))) { QString message = parser.value(QStringLiteral("ping-msg")); msg.setArguments(QVariantList() << message); } blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); } else if(parser.isSet(QStringLiteral("send-sms"))) { if (parser.isSet(QStringLiteral("destination"))) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/sms", QStringLiteral("org.kde.kdeconnect.device.sms"), QStringLiteral("sendSms")); - msg.setArguments({ parser.value("destination"), parser.value("send-sms") }); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/sms"), QStringLiteral("org.kde.kdeconnect.device.sms"), QStringLiteral("sendSms")); + msg.setArguments({ parser.value(QStringLiteral("destination")), parser.value(QStringLiteral("send-sms"))}); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); } else { QTextStream(stderr) << i18n("error: should specify the SMS's recipient by passing --destination "); return 1; } } else if(parser.isSet(QStringLiteral("ring"))) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/findmyphone", QStringLiteral("org.kde.kdeconnect.device.findmyphone"), QStringLiteral("ring")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/findmyphone"), QStringLiteral("org.kde.kdeconnect.device.findmyphone"), QStringLiteral("ring")); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); } else if(parser.isSet(QStringLiteral("photo"))) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/photo", QStringLiteral("org.kde.kdeconnect.device.photo"), QStringLiteral("requestPhoto")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/photo"), QStringLiteral("org.kde.kdeconnect.device.photo"), QStringLiteral("requestPhoto")); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); - } else if(parser.isSet("send-keys")) { - QString seq = parser.value("send-keys"); - QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kdeconnect", "/modules/kdeconnect/devices/"+device+"/remotekeyboard", "org.kde.kdeconnect.device.remotekeyboard", "sendKeyPress"); + } else if(parser.isSet(QStringLiteral("send-keys"))) { + QString seq = parser.value(QStringLiteral("send-keys")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/remotekeyboard"), QStringLiteral("org.kde.kdeconnect.device.remotekeyboard"), QStringLiteral("sendKeyPress")); if (seq.trimmed() == QLatin1String("-")) { // from stdin QFile in; if(in.open(stdin,QIODevice::ReadOnly | QIODevice::Unbuffered)) { while (!in.atEnd()) { QByteArray line = in.readLine(); // sanitize to ASCII-codes > 31? - msg.setArguments({QString(line), -1, false, false, false}); + msg.setArguments({QString::fromLatin1(line), -1, false, false, false}); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); } in.close(); } } else { msg.setArguments({seq, -1, false, false, false}); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); } } else if(parser.isSet(QStringLiteral("list-notifications"))) { NotificationsModel notifications; notifications.setDeviceId(device); for(int i=0, rows=notifications.rowCount(); itoObject(); QTextStream(stdout) << it.key() << ": " << cont.value(QStringLiteral("name")).toString() << ": " << cont.value(QStringLiteral("command")).toString() << endl; } } else if(parser.isSet(QStringLiteral("execute-command"))) { RemoteCommandsDbusInterface iface(device); blockOnReply(iface.triggerCommand(parser.value(QStringLiteral("execute-command")))); } else if(parser.isSet(QStringLiteral("encryption-info"))) { DeviceDbusInterface dev(device); QString info = blockOnReply(dev.encryptionInfo()); // QSsl::Der = 1 QTextStream(stdout) << info << endl; } else { QTextStream(stderr) << i18n("Nothing to be done") << endl; } } QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); return app.exec(); } diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp index dbcb3a88..740a288f 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.cpp +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -1,103 +1,103 @@ /** * Copyright 2016 Saikrishna Arcot * * 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 . */ #include "bluetoothdevicelink.h" #include "../linkprovider.h" #include "bluetoothlinkprovider.h" #include "bluetoothuploadjob.h" #include "bluetoothdownloadjob.h" #include "core_debug.h" BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket) : DeviceLink(deviceId, parent) , mSocketReader(new DeviceLineReader(socket, this)) , mBluetoothSocket(socket) , mPairingHandler(new BluetoothPairingHandler(this)) { connect(mSocketReader, SIGNAL(readyRead()), this, SLOT(dataReceived())); //We take ownership of the socket. //When the link provider destroys us, //the socket (and the reader) will be //destroyed as well mBluetoothSocket->setParent(this); connect(mBluetoothSocket, SIGNAL(disconnected()), this, SLOT(deleteLater())); } QString BluetoothDeviceLink::name() { - return "BluetoothLink"; // Should be same in both android and kde version + return QStringLiteral("BluetoothLink"); // Should be same in both android and kde version } bool BluetoothDeviceLink::sendPacket(NetworkPacket& np) { if (np.hasPayload()) { BluetoothUploadJob* uploadJob = new BluetoothUploadJob(np.payload(), mBluetoothSocket->peerAddress(), this); np.setPayloadTransferInfo(uploadJob->transferInfo()); uploadJob->start(); } int written = mSocketReader->write(np.serialize()); return (written != -1); } void BluetoothDeviceLink::userRequestsPair() { mPairingHandler->requestPairing(); } void BluetoothDeviceLink::userRequestsUnpair() { mPairingHandler->unpair(); } bool BluetoothDeviceLink::linkShouldBeKeptAlive() { return pairStatus() == Paired; } void BluetoothDeviceLink::dataReceived() { if (mSocketReader->bytesAvailable() == 0) return; const QByteArray serializedPacket = mSocketReader->readLine(); //qCDebug(KDECONNECT_CORE) << "BluetoothDeviceLink dataReceived" << packet; NetworkPacket packet((QString())); NetworkPacket::unserialize(serializedPacket, &packet); if (packet.type() == PACKET_TYPE_PAIR) { //TODO: Handle pair/unpair requests and forward them (to the pairing handler?) mPairingHandler->packetReceived(packet); return; } if (packet.hasPayloadTransferInfo()) { BluetoothDownloadJob* downloadJob = new BluetoothDownloadJob(mBluetoothSocket->peerAddress(), packet.payloadTransferInfo(), this); downloadJob->start(); packet.setPayload(downloadJob->payload(), packet.payloadSize()); } Q_EMIT receivedPacket(packet); if (mSocketReader->bytesAvailable() > 0) { QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); } } diff --git a/core/backends/bluetooth/bluetoothdownloadjob.cpp b/core/backends/bluetooth/bluetoothdownloadjob.cpp index f7cffc97..39e3c2b6 100644 --- a/core/backends/bluetooth/bluetoothdownloadjob.cpp +++ b/core/backends/bluetooth/bluetoothdownloadjob.cpp @@ -1,41 +1,41 @@ /* * Copyright 2016 Saikrishna Arcot * * 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 . */ #include "bluetoothdownloadjob.h" BluetoothDownloadJob::BluetoothDownloadJob(const QBluetoothAddress& remoteAddress, const QVariantMap& transferInfo, QObject* parent) : QObject(parent) , mRemoteAddress(remoteAddress) - , mTransferUuid(QBluetoothUuid(transferInfo.value("uuid").toString())) + , mTransferUuid(QBluetoothUuid(transferInfo.value(QStringLiteral("uuid")).toString())) , mSocket(new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol)) { } QSharedPointer BluetoothDownloadJob::payload() const { return mSocket.staticCast(); } void BluetoothDownloadJob::start() { connect(mSocket.data(), &QBluetoothSocket::disconnected, mSocket.data(), &QBluetoothSocket::readyRead); connect(mSocket.data(), &QBluetoothSocket::disconnected, mSocket.data(), &QBluetoothSocket::readChannelFinished); mSocket->connectToService(mRemoteAddress, mTransferUuid, QIODevice::ReadOnly); } diff --git a/core/backends/bluetooth/bluetoothlinkprovider.cpp b/core/backends/bluetooth/bluetoothlinkprovider.cpp index d20a5c4f..e69ae0a3 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.cpp +++ b/core/backends/bluetooth/bluetoothlinkprovider.cpp @@ -1,326 +1,326 @@ /** * Copyright 2016 Saikrishna Arcot * * 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 . */ #include "bluetoothlinkprovider.h" #include "core_debug.h" #include #include #include #include #include #include #include #include "bluetoothdevicelink.h" BluetoothLinkProvider::BluetoothLinkProvider() { - mServiceUuid = QBluetoothUuid(QString("185f3df4-3268-4e3f-9fca-d4d5059915bd")); + mServiceUuid = QBluetoothUuid(QStringLiteral("185f3df4-3268-4e3f-9fca-d4d5059915bd")); connectTimer = new QTimer(this); connectTimer->setInterval(30000); connectTimer->setSingleShot(false); mServiceDiscoveryAgent = new QBluetoothServiceDiscoveryAgent(this); mServiceDiscoveryAgent->setUuidFilter(mServiceUuid); connect(mServiceDiscoveryAgent, SIGNAL(finished()), this, SLOT(serviceDiscoveryFinished())); connect(connectTimer, SIGNAL(timeout()), mServiceDiscoveryAgent, SLOT(start())); } void BluetoothLinkProvider::onStart() { QBluetoothLocalDevice localDevice; if (!localDevice.isValid()) { qCWarning(KDECONNECT_CORE) << "No local bluetooth adapter found"; return; } mBluetoothServer = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); mBluetoothServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); connect(mBluetoothServer, SIGNAL(newConnection()), this, SLOT(serverNewConnection())); mServiceDiscoveryAgent->start(); connectTimer->start(); - mKdeconnectService = mBluetoothServer->listen(mServiceUuid, "KDE Connect"); + mKdeconnectService = mBluetoothServer->listen(mServiceUuid, QStringLiteral("KDE Connect")); } void BluetoothLinkProvider::onStop() { if (!mBluetoothServer) { return; } connectTimer->stop(); mKdeconnectService.unregisterService(); mBluetoothServer->close(); mBluetoothServer->deleteLater(); } //I'm in a new network, let's be polite and introduce myself void BluetoothLinkProvider::onNetworkChange() { } void BluetoothLinkProvider::connectError() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; qCWarning(KDECONNECT_CORE) << "Couldn't connect to socket:" << socket->errorString(); disconnect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); disconnect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); mSockets.remove(socket->peerAddress()); socket->deleteLater(); } void BluetoothLinkProvider::addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId) { QMap< QString, DeviceLink* >::iterator oldLinkIterator = mLinks.find(deviceId); if (oldLinkIterator != mLinks.end()) { DeviceLink* oldLink = oldLinkIterator.value(); disconnect(oldLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); oldLink->deleteLater(); mLinks.erase(oldLinkIterator); } mLinks[deviceId] = deviceLink; } //I'm the new device and I found an existing device void BluetoothLinkProvider::serviceDiscoveryFinished() { const QList services = mServiceDiscoveryAgent->discoveredServices(); for (QBluetoothServiceInfo info : services) { if (mSockets.contains(info.device().address())) { continue; } QBluetoothSocket* socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); connect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); socket->connectToService(info); qCDebug(KDECONNECT_CORE()) << "Connecting to" << info.device().address(); if (socket->error() != QBluetoothSocket::NoSocketError) { qCWarning(KDECONNECT_CORE) << "Socket connection error:" << socket->errorString(); } } } //I'm the new device and I'm connected to the existing device. Time to get data. void BluetoothLinkProvider::clientConnected() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; qCDebug(KDECONNECT_CORE()) << "Connected to" << socket->peerAddress(); if (mSockets.contains(socket->peerAddress())) { qCWarning(KDECONNECT_CORE()) << "Duplicate connection to" << socket->peerAddress(); socket->close(); socket->deleteLater(); return; } mSockets.insert(socket->peerAddress(), socket); disconnect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(clientIdentityReceived())); connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); } //I'm the new device and the existing device sent me data. void BluetoothLinkProvider::clientIdentityReceived() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; QByteArray identityArray; if (socket->property("identityArray").isValid()) { identityArray = socket->property("identityArray").toByteArray(); } while (!identityArray.contains('\n') && socket->bytesAvailable() > 0) { identityArray += socket->readAll(); } if (!identityArray.contains('\n')) { // This method will get retriggered when more data is available. socket->setProperty("identityArray", identityArray); return; } socket->setProperty("identityArray", QVariant()); disconnect(socket, SIGNAL(readyRead()), this, SLOT(clientIdentityReceived())); - NetworkPacket receivedPacket(""); + NetworkPacket receivedPacket; bool success = NetworkPacket::unserialize(identityArray, &receivedPacket); if (!success || receivedPacket.type() != PACKET_TYPE_IDENTITY) { qCWarning(KDECONNECT_CORE) << "Not an identity packet"; mSockets.remove(socket->peerAddress()); socket->close(); socket->deleteLater(); return; } qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << socket->peerAddress(); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); - const QString& deviceId = receivedPacket.get("deviceId"); + const QString& deviceId = receivedPacket.get(QStringLiteral("deviceId")); BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, socket); - NetworkPacket np2(""); + NetworkPacket np2; NetworkPacket::createIdentityPacket(&np2); success = deviceLink->sendPacket(np2); if (success) { qCDebug(KDECONNECT_CORE) << "Handshaking done (I'm the new device)"; connect(deviceLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); Q_EMIT onConnectionReceived(receivedPacket, deviceLink); //We kill any possible link from this same device addLink(deviceLink, deviceId); } else { // Connection might be lost. Delete it. delete deviceLink; } //We don't delete the socket because now it's owned by the BluetoothDeviceLink } //I'm the existing device, a new device is kindly introducing itself. void BluetoothLinkProvider::serverNewConnection() { QBluetoothSocket* socket = mBluetoothServer->nextPendingConnection(); qCDebug(KDECONNECT_CORE()) << "Received connection from" << socket->peerAddress(); if (mSockets.contains(socket->peerAddress())) { qCDebug(KDECONNECT_CORE()) << "Duplicate connection from" << socket->peerAddress(); socket->close(); socket->deleteLater(); return; } connect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); - NetworkPacket np2(""); + NetworkPacket np2; NetworkPacket::createIdentityPacket(&np2); socket->write(np2.serialize()); qCDebug(KDECONNECT_CORE()) << "Sent identity packet to" << socket->peerAddress(); mSockets.insert(socket->peerAddress(), socket); } //I'm the existing device and this is the answer to my identity packet (data received) void BluetoothLinkProvider::serverDataReceived() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; QByteArray identityArray; if (socket->property("identityArray").isValid()) { identityArray = socket->property("identityArray").toByteArray(); } while (!identityArray.contains('\n') && socket->bytesAvailable() > 0) { identityArray += socket->readAll(); } if (!identityArray.contains('\n')) { // This method will get retriggered when more data is available. socket->setProperty("identityArray", identityArray); return; } socket->setProperty("identityArray", QVariant()); disconnect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); - NetworkPacket receivedPacket(""); + NetworkPacket receivedPacket; bool success = NetworkPacket::unserialize(identityArray, &receivedPacket); if (!success || receivedPacket.type() != PACKET_TYPE_IDENTITY) { qCWarning(KDECONNECT_CORE) << "Not an identity packet."; mSockets.remove(socket->peerAddress()); socket->close(); socket->deleteLater(); return; } qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << socket->peerAddress(); - const QString& deviceId = receivedPacket.get("deviceId"); + const QString& deviceId = receivedPacket.get(QStringLiteral("deviceId")); BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, socket); connect(deviceLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); Q_EMIT onConnectionReceived(receivedPacket, deviceLink); addLink(deviceLink, deviceId); } void BluetoothLinkProvider::deviceLinkDestroyed(QObject* destroyedDeviceLink) { const QString id = destroyedDeviceLink->property("deviceId").toString(); qCDebug(KDECONNECT_CORE()) << "Device disconnected:" << id; QMap< QString, DeviceLink* >::iterator oldLinkIterator = mLinks.find(id); if (oldLinkIterator != mLinks.end() && oldLinkIterator.value() == destroyedDeviceLink) { mLinks.erase(oldLinkIterator); } } void BluetoothLinkProvider::socketDisconnected() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; disconnect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); mSockets.remove(mSockets.key(socket)); } BluetoothLinkProvider::~BluetoothLinkProvider() { } diff --git a/core/backends/bluetooth/bluetoothlinkprovider.h b/core/backends/bluetooth/bluetoothlinkprovider.h index d7815914..744ce573 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.h +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -1,83 +1,83 @@ /** * Copyright 2016 Saikrishna Arcot * * 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 . */ #ifndef BLUETOOTHLINKPROVIDER_H #define BLUETOOTHLINKPROVIDER_H #include #include #include #include #include #include #include #include #include #include #include "../linkprovider.h" class BluetoothDeviceLink; class KDECONNECTCORE_EXPORT BluetoothLinkProvider : public LinkProvider { Q_OBJECT public: BluetoothLinkProvider(); virtual ~BluetoothLinkProvider(); - QString name() override { return "BluetoothLinkProvider"; } + QString name() override { return QStringLiteral("BluetoothLinkProvider"); } int priority() override { return PRIORITY_MEDIUM; } public Q_SLOTS: virtual void onNetworkChange() override; virtual void onStart() override; virtual void onStop() override; void connectError(); void serviceDiscoveryFinished(); private Q_SLOTS: void deviceLinkDestroyed(QObject* destroyedDeviceLink); void socketDisconnected(); void serverNewConnection(); void serverDataReceived(); void clientConnected(); void clientIdentityReceived(); private: void addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId); QList getPairedDevices(); QBluetoothUuid mServiceUuid; QPointer mBluetoothServer; QBluetoothServiceInfo mKdeconnectService; QBluetoothServiceDiscoveryAgent* mServiceDiscoveryAgent; QTimer* connectTimer; QMap mLinks; QMap mSockets; }; #endif diff --git a/core/backends/bluetooth/bluetoothpairinghandler.cpp b/core/backends/bluetooth/bluetoothpairinghandler.cpp index 3eb2d452..8889cd2e 100644 --- a/core/backends/bluetooth/bluetoothpairinghandler.cpp +++ b/core/backends/bluetooth/bluetoothpairinghandler.cpp @@ -1,152 +1,152 @@ /** * Copyright 2015 Vineet Garg * * 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 . */ #include "bluetoothpairinghandler.h" #include #include "core_debug.h" #include "daemon.h" #include "kdeconnectconfig.h" #include "networkpackettypes.h" BluetoothPairingHandler::BluetoothPairingHandler(DeviceLink* deviceLink) : PairingHandler(deviceLink) , m_status(NotPaired) { m_pairingTimeout.setSingleShot(true); m_pairingTimeout.setInterval(pairingTimeoutMsec()); connect(&m_pairingTimeout, &QTimer::timeout, this, &BluetoothPairingHandler::pairingTimeout); } void BluetoothPairingHandler::packetReceived(const NetworkPacket& np) { qCDebug(KDECONNECT_CORE) << "Pairing packet received!" << np.serialize(); m_pairingTimeout.stop(); - bool wantsPair = np.get("pair"); + bool wantsPair = np.get(QStringLiteral("pair")); if (wantsPair) { if (isPairRequested()) { //We started pairing qCDebug(KDECONNECT_CORE) << "Pair answer"; setInternalPairStatus(Paired); } else { qCDebug(KDECONNECT_CORE) << "Pair request"; if (isPaired()) { //I'm already paired, but they think I'm not acceptPairing(); return; } setInternalPairStatus(RequestedByPeer); } } else { //wantsPair == false qCDebug(KDECONNECT_CORE) << "Unpair request"; setInternalPairStatus(NotPaired); if (isPairRequested()) { Q_EMIT pairingError(i18n("Canceled by other peer")); } } } bool BluetoothPairingHandler::requestPairing() { switch (m_status) { case Paired: Q_EMIT pairingError(i18n("%1: Already paired", deviceLink()->name())); return false; case Requested: Q_EMIT pairingError(i18n("%1: Pairing already requested for this device", deviceLink()->name())); return false; case RequestedByPeer: qCDebug(KDECONNECT_CORE) << deviceLink()->name() << " : Pairing already started by the other end, accepting their request."; acceptPairing(); return false; case NotPaired: ; } NetworkPacket np(PACKET_TYPE_PAIR); - np.set("pair", true); + np.set(QStringLiteral("pair"), true); bool success; success = deviceLink()->sendPacket(np); if (success) { setInternalPairStatus(Requested); m_pairingTimeout.start(); } return success; } bool BluetoothPairingHandler::acceptPairing() { qCDebug(KDECONNECT_CORE) << "User accepts pairing"; m_pairingTimeout.stop(); // Just in case it is started NetworkPacket np(PACKET_TYPE_PAIR); - np.set("pair", true); + np.set(QStringLiteral("pair"), true); bool success = deviceLink()->sendPacket(np); if (success) { setInternalPairStatus(Paired); } return success; } void BluetoothPairingHandler::rejectPairing() { qCDebug(KDECONNECT_CORE) << "User rejects pairing"; NetworkPacket np(PACKET_TYPE_PAIR); - np.set("pair", false); + np.set(QStringLiteral("pair"), false); deviceLink()->sendPacket(np); setInternalPairStatus(NotPaired); } void BluetoothPairingHandler::unpair() { NetworkPacket np(PACKET_TYPE_PAIR); - np.set("pair", false); + np.set(QStringLiteral("pair"), false); deviceLink()->sendPacket(np); setInternalPairStatus(NotPaired); } void BluetoothPairingHandler::pairingTimeout() { NetworkPacket np(PACKET_TYPE_PAIR); - np.set("pair", false); + np.set(QStringLiteral("pair"), false); deviceLink()->sendPacket(np); setInternalPairStatus(NotPaired); //Will emit the change as well Q_EMIT pairingError(i18n("Timed out")); } void BluetoothPairingHandler::setInternalPairStatus(BluetoothPairingHandler::InternalPairStatus status) { m_status = status; if (status == Paired) { deviceLink()->setPairStatus(DeviceLink::Paired); } else if (status == NotPaired){ deviceLink()->setPairStatus(DeviceLink::NotPaired); } else if (status == RequestedByPeer) { Q_EMIT deviceLink()->pairingRequest(this); } } diff --git a/core/backends/bluetooth/bluetoothuploadjob.cpp b/core/backends/bluetooth/bluetoothuploadjob.cpp index d7b56478..f8cfaec1 100644 --- a/core/backends/bluetooth/bluetoothuploadjob.cpp +++ b/core/backends/bluetooth/bluetoothuploadjob.cpp @@ -1,113 +1,113 @@ /* * Copyright 2016 Saikrishna Arcot * Copyright 2018 Matthijs TIjink * * 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 . */ #include "bluetoothuploadjob.h" #include #include "core_debug.h" #include BluetoothUploadJob::BluetoothUploadJob(const QSharedPointer& data, const QBluetoothAddress& remoteAddress, QObject* parent) : QObject(parent) , mData(data) , mRemoteAddress(remoteAddress) , mTransferUuid(QBluetoothUuid::createUuid()) , mServer(new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this)) { mServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); } QVariantMap BluetoothUploadJob::transferInfo() const { QVariantMap ret; - ret["uuid"] = mTransferUuid.toString().mid(1, 36); + ret[QStringLiteral("uuid")] = mTransferUuid.toString().mid(1, 36); return ret; } void BluetoothUploadJob::start() { connect(mServer, &QBluetoothServer::newConnection, this, &BluetoothUploadJob::newConnection); - mServiceInfo = mServer->listen(mTransferUuid, "KDE Connect Transfer Job"); + mServiceInfo = mServer->listen(mTransferUuid, QStringLiteral("KDE Connect Transfer Job")); Q_ASSERT(mServiceInfo.isValid()); } void BluetoothUploadJob::newConnection() { m_socket = mServer->nextPendingConnection(); Q_ASSERT(m_socket); m_socket->setParent(this); connect(m_socket, &QBluetoothSocket::disconnected, this, &BluetoothUploadJob::deleteLater); if (m_socket->peerAddress() != mRemoteAddress) { m_socket->close(); } else { mServer->close(); disconnect(mServer, &QBluetoothServer::newConnection, this, &BluetoothUploadJob::newConnection); mServiceInfo.unregisterService(); if (!mData->open(QIODevice::ReadOnly)) { qCWarning(KDECONNECT_CORE) << "error when opening the input to upload"; m_socket->close(); deleteLater(); return; } } connect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::writeSome); connect(m_socket, &QBluetoothSocket::disconnected, this, &BluetoothUploadJob::closeConnection); writeSome(); } void BluetoothUploadJob::writeSome() { Q_ASSERT(m_socket); bool errorOccurred = false; while (m_socket->bytesToWrite() == 0 && mData->bytesAvailable() && m_socket->isWritable()) { qint64 bytes = qMin(mData->bytesAvailable(), 4096); int bytesWritten = m_socket->write(mData->read(bytes)); if (bytesWritten < 0) { qCWarning(KDECONNECT_CORE()) << "error when writing data to bluetooth upload" << bytes << mData->bytesAvailable(); errorOccurred = true; break; } } if (mData->atEnd() || errorOccurred) { disconnect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::writeSome); mData->close(); connect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::finishWrites); finishWrites(); } } void BluetoothUploadJob::finishWrites() { Q_ASSERT(m_socket); if (m_socket->bytesToWrite() == 0) { closeConnection(); } } void BluetoothUploadJob::closeConnection() { mData->close(); deleteLater(); } diff --git a/core/backends/lan/compositeuploadjob.cpp b/core/backends/lan/compositeuploadjob.cpp index 614d3e35..a0e61bf3 100644 --- a/core/backends/lan/compositeuploadjob.cpp +++ b/core/backends/lan/compositeuploadjob.cpp @@ -1,295 +1,295 @@ /** * Copyright 2018 Erik Duisters * * 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 . */ #include "compositeuploadjob.h" #include #include #include #include #include "lanlinkprovider.h" #include #include "plugins/share/shareplugin.h" CompositeUploadJob::CompositeUploadJob(const QString& deviceId, bool displayNotification) : KCompositeJob() , m_server(new Server(this)) , m_socket(nullptr) , m_port(0) , m_deviceId(deviceId) , m_running(false) , m_currentJobNum(1) , m_totalJobs(0) , m_currentJobSendPayloadSize(0) , m_totalSendPayloadSize(0) , m_totalPayloadSize(0) , m_currentJob(nullptr) , m_prevElapsedTime(0) , m_updatePacketPending(false) { setCapabilities(Killable); if (displayNotification) { KIO::getJobTracker()->registerJob(this); } } bool CompositeUploadJob::isRunning() { return m_running; } void CompositeUploadJob::start() { if (m_running) { qCWarning(KDECONNECT_CORE) << "CompositeUploadJob::start() - already running"; return; } if (!hasSubjobs()) { qCWarning(KDECONNECT_CORE) << "CompositeUploadJob::start() - there are no subjobs to start"; emitResult(); return; } if (!startListening()) { return; } connect(m_server, &QTcpServer::newConnection, this, &CompositeUploadJob::newConnection); m_running = true; //Give SharePlugin some time to add subjobs QMetaObject::invokeMethod(this, "startNextSubJob", Qt::QueuedConnection); } bool CompositeUploadJob::startListening() { m_port = MIN_PORT; while (!m_server->listen(QHostAddress::Any, m_port)) { m_port++; if (m_port > MAX_PORT) { //No ports available? qCWarning(KDECONNECT_CORE) << "CompositeUploadJob::startListening() - Error opening a port in range" << MIN_PORT << "-" << MAX_PORT; m_port = 0; setError(NoPortAvailable); setErrorText(i18n("Couldn't find an available port")); emitResult(); return false; } } qCDebug(KDECONNECT_CORE) << "CompositeUploadJob::startListening() - listening on port: " << m_port; return true; } void CompositeUploadJob::startNextSubJob() { m_currentJob = qobject_cast(subjobs().at(0)); m_currentJobSendPayloadSize = 0; emitDescription(m_currentJob->getNetworkPacket().get(QStringLiteral("filename"))); connect(m_currentJob, SIGNAL(processedAmount(KJob*,KJob::Unit,qulonglong)), this, SLOT(slotProcessedAmount(KJob*,KJob::Unit,qulonglong))); //Already done by KCompositeJob //connect(m_currentJob, &KJob::result, this, &CompositeUploadJob::slotResult); //TODO: Create a copy of the networkpacket that can be re-injected if sending via lan fails? NetworkPacket np = m_currentJob->getNetworkPacket(); np.setPayload(nullptr, np.payloadSize()); - np.setPayloadTransferInfo({{"port", m_port}}); + np.setPayloadTransferInfo({{QStringLiteral("port"), m_port}}); np.set(QStringLiteral("numberOfFiles"), m_totalJobs); np.set(QStringLiteral("totalPayloadSize"), m_totalPayloadSize); if (Daemon::instance()->getDevice(m_deviceId)->sendPacket(np)) { m_server->resumeAccepting(); } else { setError(SendingNetworkPacketFailed); setErrorText(i18n("Failed to send packet to %1", Daemon::instance()->getDevice(m_deviceId)->name())); emitResult(); } } void CompositeUploadJob::newConnection() { m_server->pauseAccepting(); m_socket = m_server->nextPendingConnection(); if (!m_socket) { qCDebug(KDECONNECT_CORE) << "CompositeUploadJob::newConnection() - m_server->nextPendingConnection() returned a nullptr"; return; } m_currentJob->setSocket(m_socket); connect(m_socket, &QSslSocket::disconnected, this, &CompositeUploadJob::socketDisconnected); connect(m_socket, QOverload::of(&QAbstractSocket::error), this, &CompositeUploadJob::socketError); connect(m_socket, QOverload &>::of(&QSslSocket::sslErrors), this, &CompositeUploadJob::sslError); connect(m_socket, &QSslSocket::encrypted, this, &CompositeUploadJob::encrypted); LanLinkProvider::configureSslSocket(m_socket, m_deviceId, true); m_socket->startServerEncryption(); } void CompositeUploadJob::socketDisconnected() { m_socket->close(); } void CompositeUploadJob::socketError(QAbstractSocket::SocketError error) { Q_UNUSED(error); //Do not close the socket because when android closes the socket (share is cancelled) closing the socket leads to a cyclic socketError and eventually a segv setError(SocketError); emitResult(); m_running = false; } void CompositeUploadJob::sslError(const QList& errors) { Q_UNUSED(errors); m_socket->close(); setError(SslError); emitResult(); m_running = false; } void CompositeUploadJob::encrypted() { if (!m_timer.isValid()) { m_timer.start(); } m_currentJob->start(); } bool CompositeUploadJob::addSubjob(KJob* job) { if (UploadJob *uploadJob = qobject_cast(job)) { NetworkPacket np = uploadJob->getNetworkPacket(); m_totalJobs++; if (np.payloadSize() >= 0 ) { m_totalPayloadSize += np.payloadSize(); setTotalAmount(Bytes, m_totalPayloadSize); } QString filename; QString filenameArg = QStringLiteral("filename"); if (m_currentJob) { filename = m_currentJob->getNetworkPacket().get(filenameArg); } else { filename = np.get(filenameArg); } emitDescription(filename); if (m_running && m_currentJob && !m_updatePacketPending) { m_updatePacketPending = true; QMetaObject::invokeMethod(this, "sendUpdatePacket", Qt::QueuedConnection); } return KCompositeJob::addSubjob(job); } else { qCDebug(KDECONNECT_CORE) << "CompositeUploadJob::addSubjob() - you can only add UploadJob's, ignoring"; return false; } } void CompositeUploadJob::sendUpdatePacket() { NetworkPacket np(PACKET_TYPE_SHARE_REQUEST_UPDATE); np.set(QStringLiteral("numberOfFiles"), m_totalJobs); np.set(QStringLiteral("totalPayloadSize"), m_totalPayloadSize); Daemon::instance()->getDevice(m_deviceId)->sendPacket(np); m_updatePacketPending = false; } bool CompositeUploadJob::doKill() { if (m_running) { m_running = false; return m_currentJob->stop(); } return true; } void CompositeUploadJob::slotProcessedAmount(KJob *job, KJob::Unit unit, qulonglong amount) { Q_UNUSED(job); m_currentJobSendPayloadSize = amount; quint64 uploaded = m_totalSendPayloadSize + m_currentJobSendPayloadSize; if (uploaded == m_totalPayloadSize || m_prevElapsedTime == 0 || m_timer.elapsed() - m_prevElapsedTime >= 100) { m_prevElapsedTime = m_timer.elapsed(); setProcessedAmount(unit, uploaded); const auto elapsed = m_timer.elapsed(); if (elapsed > 0) { emitSpeed((1000 * uploaded) / elapsed); } } } void CompositeUploadJob::slotResult(KJob *job) { //Copies job error and errorText and emits result if job is in error otherwise removes job from subjob list KCompositeJob::slotResult(job); if (error() || !m_running) { return; } m_totalSendPayloadSize += m_currentJobSendPayloadSize; if (hasSubjobs()) { m_currentJobNum++; startNextSubJob(); } else { QPair field2; - field2.first = QString("Files"); + field2.first = QStringLiteral("Files"); field2.second = i18np("Sent 1 file", "Sent %1 files", m_totalJobs); Q_EMIT description(this, i18n("Sending to %1", Daemon::instance()->getDevice(this->m_deviceId)->name()), { QString(), QString() }, field2 ); emitResult(); } } void CompositeUploadJob::emitDescription(const QString& currentFileName) { QPair field2; if (m_totalJobs > 1) { field2.first = i18n("Progress"); field2.second = i18n("Sending file %1 of %2", m_currentJobNum, m_totalJobs); } Q_EMIT description(this, i18n("Sending to %1", Daemon::instance()->getDevice(this->m_deviceId)->name()), { i18n("File"), currentFileName }, field2 ); } diff --git a/core/backends/lan/landevicelink.cpp b/core/backends/lan/landevicelink.cpp index c1e67437..3b157e47 100644 --- a/core/backends/lan/landevicelink.cpp +++ b/core/backends/lan/landevicelink.cpp @@ -1,194 +1,194 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "landevicelink.h" #include #include "core_debug.h" #include "kdeconnectconfig.h" #include "backends/linkprovider.h" #include "socketlinereader.h" #include "lanlinkprovider.h" #include "plugins/share/shareplugin.h" LanDeviceLink::LanDeviceLink(const QString& deviceId, LinkProvider* parent, QSslSocket* socket, ConnectionStarted connectionSource) : DeviceLink(deviceId, parent) , m_socketLineReader(nullptr) { reset(socket, connectionSource); } void LanDeviceLink::reset(QSslSocket* socket, ConnectionStarted connectionSource) { if (m_socketLineReader) { disconnect(m_socketLineReader->m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); delete m_socketLineReader; } m_socketLineReader = new SocketLineReader(socket, this); connect(socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater); connect(m_socketLineReader, &SocketLineReader::readyRead, this, &LanDeviceLink::dataReceived); //We take ownership of the socket. //When the link provider destroys us, //the socket (and the reader) will be //destroyed as well socket->setParent(m_socketLineReader); m_connectionSource = connectionSource; QString certString = KdeConnectConfig::instance()->getDeviceProperty(deviceId(), QStringLiteral("certificate")); DeviceLink::setPairStatus(certString.isEmpty()? PairStatus::NotPaired : PairStatus::Paired); } QHostAddress LanDeviceLink::hostAddress() const { if (!m_socketLineReader) { return QHostAddress::Null; } QHostAddress addr = m_socketLineReader->m_socket->peerAddress(); if (addr.protocol() == QAbstractSocket::IPv6Protocol) { bool success; QHostAddress convertedAddr = QHostAddress(addr.toIPv4Address(&success)); if (success) { qCDebug(KDECONNECT_CORE) << "Converting IPv6" << addr << "to IPv4" << convertedAddr; addr = convertedAddr; } } return addr; } QString LanDeviceLink::name() { return QStringLiteral("LanLink"); // Should be same in both android and kde version } bool LanDeviceLink::sendPacket(NetworkPacket& np) { if (np.payload()) { if (np.type() == PACKET_TYPE_SHARE_REQUEST && np.payloadSize() >= 0) { if (!m_compositeUploadJob || !m_compositeUploadJob->isRunning()) { m_compositeUploadJob = new CompositeUploadJob(deviceId(), true); } m_compositeUploadJob->addSubjob(new UploadJob(np)); if (!m_compositeUploadJob->isRunning()) { m_compositeUploadJob->start(); } } else { //Infinite stream CompositeUploadJob* fireAndForgetJob = new CompositeUploadJob(deviceId(), false); fireAndForgetJob->addSubjob(new UploadJob(np)); fireAndForgetJob->start(); } return true; } else { int written = m_socketLineReader->write(np.serialize()); //Actually we can't detect if a packet is received or not. We keep TCP //"ESTABLISHED" connections that look legit (return true when we use them), //but that are actually broken (until keepalive detects that they are down). return (written != -1); } } void LanDeviceLink::dataReceived() { if (m_socketLineReader->bytesAvailable() == 0) return; const QByteArray serializedPacket = m_socketLineReader->readLine(); NetworkPacket packet((QString())); NetworkPacket::unserialize(serializedPacket, &packet); //qCDebug(KDECONNECT_CORE) << "LanDeviceLink dataReceived" << serializedPacket; if (packet.type() == PACKET_TYPE_PAIR) { //TODO: Handle pair/unpair requests and forward them (to the pairing handler?) qobject_cast(provider())->incomingPairPacket(this, packet); return; } if (packet.hasPayloadTransferInfo()) { //qCDebug(KDECONNECT_CORE) << "HasPayloadTransferInfo"; const QVariantMap transferInfo = packet.payloadTransferInfo(); QSharedPointer socket(new QSslSocket); LanLinkProvider::configureSslSocket(socket.data(), deviceId(), true); // emit readChannelFinished when the socket gets disconnected. This seems to be a bug in upstream QSslSocket. // Needs investigation and upstreaming of the fix. QTBUG-62257 connect(socket.data(), &QAbstractSocket::disconnected, socket.data(), &QAbstractSocket::readChannelFinished); const QString address = m_socketLineReader->peerAddress().toString(); const quint16 port = transferInfo[QStringLiteral("port")].toInt(); socket->connectToHostEncrypted(address, port, QIODevice::ReadWrite); packet.setPayload(socket, packet.payloadSize()); } Q_EMIT receivedPacket(packet); if (m_socketLineReader->bytesAvailable() > 0) { QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); } } void LanDeviceLink::userRequestsPair() { if (m_socketLineReader->peerCertificate().isNull()) { Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect.")); } else { qobject_cast(provider())->userRequestsPair(deviceId()); } } void LanDeviceLink::userRequestsUnpair() { qobject_cast(provider())->userRequestsUnpair(deviceId()); } void LanDeviceLink::setPairStatus(PairStatus status) { if (status == Paired && m_socketLineReader->peerCertificate().isNull()) { Q_EMIT pairingError(i18n("This device cannot be paired because it is running an old version of KDE Connect.")); return; } DeviceLink::setPairStatus(status); if (status == Paired) { Q_ASSERT(KdeConnectConfig::instance()->trustedDevices().contains(deviceId())); Q_ASSERT(!m_socketLineReader->peerCertificate().isNull()); - KdeConnectConfig::instance()->setDeviceProperty(deviceId(), QStringLiteral("certificate"), m_socketLineReader->peerCertificate().toPem()); + KdeConnectConfig::instance()->setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(m_socketLineReader->peerCertificate().toPem().data())); } } bool LanDeviceLink::linkShouldBeKeptAlive() { return true; //FIXME: Current implementation is broken, so for now we will keep links always established //We keep the remotely initiated connections, since the remotes require them if they want to request //pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing //return (mConnectionSource == ConnectionStarted::Remotely || pairStatus() == Paired); } diff --git a/core/backends/lan/lanlinkprovider.cpp b/core/backends/lan/lanlinkprovider.cpp index 572ac3f8..1893ccff 100644 --- a/core/backends/lan/lanlinkprovider.cpp +++ b/core/backends/lan/lanlinkprovider.cpp @@ -1,558 +1,558 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "lanlinkprovider.h" #include "core_debug.h" #ifndef Q_OS_WIN #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include "daemon.h" #include "landevicelink.h" #include "lanpairinghandler.h" #include "kdeconnectconfig.h" #define MIN_VERSION_WITH_SSL_SUPPORT 6 LanLinkProvider::LanLinkProvider( bool testMode, quint16 udpBroadcastPort, quint16 udpListenPort ) : m_server(new Server(this)) , m_udpSocket(this) , m_tcpPort(0) , m_udpBroadcastPort(udpBroadcastPort) , m_udpListenPort(udpListenPort) , m_testMode(testMode) , m_combineBroadcastsTimer(this) { m_combineBroadcastsTimer.setInterval(0); // increase this if waiting a single event-loop iteration is not enough m_combineBroadcastsTimer.setSingleShot(true); connect(&m_combineBroadcastsTimer, &QTimer::timeout, this, &LanLinkProvider::broadcastToNetwork); connect(&m_udpSocket, &QIODevice::readyRead, this, &LanLinkProvider::udpBroadcastReceived); m_server->setProxy(QNetworkProxy::NoProxy); connect(m_server, &QTcpServer::newConnection, this, &LanLinkProvider::newConnection); m_udpSocket.setProxy(QNetworkProxy::NoProxy); //Detect when a network interface changes status, so we announce ourselves in the new network QNetworkConfigurationManager* networkManager = new QNetworkConfigurationManager(this); connect(networkManager, &QNetworkConfigurationManager::configurationChanged, this, &LanLinkProvider::onNetworkConfigurationChanged); } void LanLinkProvider::onNetworkConfigurationChanged(const QNetworkConfiguration& config) { if (m_lastConfig != config && config.state() == QNetworkConfiguration::Active) { m_lastConfig = config; onNetworkChange(); } } LanLinkProvider::~LanLinkProvider() { } void LanLinkProvider::onStart() { const QHostAddress bindAddress = m_testMode? QHostAddress::LocalHost : QHostAddress::Any; bool success = m_udpSocket.bind(bindAddress, m_udpListenPort, QUdpSocket::ShareAddress); if (!success) { QAbstractSocket::SocketError sockErr = m_udpSocket.error(); // Refer to https://doc.qt.io/qt-5/qabstractsocket.html#SocketError-enum to decode socket error number - QString errorMessage = QMetaEnum::fromType().valueToKey(sockErr); + QString errorMessage = QString::fromLatin1(QMetaEnum::fromType().valueToKey(sockErr)); qCritical(KDECONNECT_CORE) << QLatin1String("Failed to bind UDP socket on port") << m_udpListenPort << QLatin1String("with error") << errorMessage; } Q_ASSERT(success); m_tcpPort = MIN_TCP_PORT; while (!m_server->listen(bindAddress, m_tcpPort)) { m_tcpPort++; if (m_tcpPort > MAX_TCP_PORT) { //No ports available? qCritical(KDECONNECT_CORE) << "Error opening a port in range" << MIN_TCP_PORT << "-" << MAX_TCP_PORT; m_tcpPort = 0; return; } } onNetworkChange(); qCDebug(KDECONNECT_CORE) << "LanLinkProvider started"; } void LanLinkProvider::onStop() { m_udpSocket.close(); m_server->close(); qCDebug(KDECONNECT_CORE) << "LanLinkProvider stopped"; } void LanLinkProvider::onNetworkChange() { if (m_combineBroadcastsTimer.isActive()) { qCDebug(KDECONNECT_CORE()) << "Preventing duplicate broadcasts"; return; } m_combineBroadcastsTimer.start(); } //I'm in a new network, let's be polite and introduce myself void LanLinkProvider::broadcastToNetwork() { if (!m_server->isListening()) { //Not started return; } Q_ASSERT(m_tcpPort != 0); qCDebug(KDECONNECT_CORE()) << "Broadcasting identity packet"; QHostAddress destAddress = m_testMode? QHostAddress::LocalHost : QHostAddress(QStringLiteral("255.255.255.255")); NetworkPacket np(QLatin1String("")); NetworkPacket::createIdentityPacket(&np); np.set(QStringLiteral("tcpPort"), m_tcpPort); #ifdef Q_OS_WIN //On Windows we need to broadcast from every local IP address to reach all networks QUdpSocket sendSocket; sendSocket.setProxy(QNetworkProxy::NoProxy); for (const QNetworkInterface& iface : QNetworkInterface::allInterfaces()) { if ( (iface.flags() & QNetworkInterface::IsUp) && (iface.flags() & QNetworkInterface::IsRunning) && (iface.flags() & QNetworkInterface::CanBroadcast)) { for (const QNetworkAddressEntry& ifaceAddress : iface.addressEntries()) { QHostAddress sourceAddress = ifaceAddress.ip(); if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) { qCDebug(KDECONNECT_CORE()) << "Broadcasting as" << sourceAddress; sendSocket.writeDatagram(np.serialize(), destAddress, m_udpBroadcastPort); sendSocket.close(); } } } } #else m_udpSocket.writeDatagram(np.serialize(), destAddress, m_udpBroadcastPort); #endif } //I'm the existing device, a new device is kindly introducing itself. //I will create a TcpSocket and try to connect. This can result in either tcpSocketConnected() or connectError(). void LanLinkProvider::udpBroadcastReceived() { while (m_udpSocket.hasPendingDatagrams()) { QByteArray datagram; datagram.resize(m_udpSocket.pendingDatagramSize()); QHostAddress sender; m_udpSocket.readDatagram(datagram.data(), datagram.size(), &sender); if (sender.isLoopback() && !m_testMode) continue; NetworkPacket* receivedPacket = new NetworkPacket(QLatin1String("")); bool success = NetworkPacket::unserialize(datagram, receivedPacket); //qCDebug(KDECONNECT_CORE) << "udp connection from " << receivedPacket->; //qCDebug(KDECONNECT_CORE) << "Datagram " << datagram.data() ; if (!success) { qCDebug(KDECONNECT_CORE) << "Could not unserialize UDP packet"; delete receivedPacket; continue; } if (receivedPacket->type() != PACKET_TYPE_IDENTITY) { qCDebug(KDECONNECT_CORE) << "Received a UDP packet of wrong type" << receivedPacket->type(); delete receivedPacket; continue; } if (receivedPacket->get(QStringLiteral("deviceId")) == KdeConnectConfig::instance()->deviceId()) { //qCDebug(KDECONNECT_CORE) << "Ignoring my own broadcast"; delete receivedPacket; continue; } int tcpPort = receivedPacket->get(QStringLiteral("tcpPort")); //qCDebug(KDECONNECT_CORE) << "Received Udp identity packet from" << sender << " asking for a tcp connection on port " << tcpPort; QSslSocket* socket = new QSslSocket(this); socket->setProxy(QNetworkProxy::NoProxy); m_receivedIdentityPackets[socket].np = receivedPacket; m_receivedIdentityPackets[socket].sender = sender; connect(socket, &QAbstractSocket::connected, this, &LanLinkProvider::tcpSocketConnected); connect(socket, QOverload::of(&QAbstractSocket::error), this, &LanLinkProvider::connectError); socket->connectToHost(sender, tcpPort); } } void LanLinkProvider::connectError(QAbstractSocket::SocketError socketError) { QSslSocket* socket = qobject_cast(sender()); if (!socket) return; qCDebug(KDECONNECT_CORE) << "Socket error" << socketError; qCDebug(KDECONNECT_CORE) << "Fallback (1), try reverse connection (send udp packet)" << socket->errorString(); NetworkPacket np(QLatin1String("")); NetworkPacket::createIdentityPacket(&np); np.set(QStringLiteral("tcpPort"), m_tcpPort); m_udpSocket.writeDatagram(np.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort); //The socket we created didn't work, and we didn't manage //to create a LanDeviceLink from it, deleting everything. delete m_receivedIdentityPackets.take(socket).np; delete socket; } //We received a UDP packet and answered by connecting to them by TCP. This gets called on a successful connection. void LanLinkProvider::tcpSocketConnected() { QSslSocket* socket = qobject_cast(sender()); if (!socket) return; // TODO Delete me? disconnect(socket, QOverload::of(&QAbstractSocket::error), this, &LanLinkProvider::connectError); configureSocket(socket); // If socket disconnects due to any reason after connection, link on ssl failure connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); NetworkPacket* receivedPacket = m_receivedIdentityPackets[socket].np; const QString& deviceId = receivedPacket->get(QStringLiteral("deviceId")); //qCDebug(KDECONNECT_CORE) << "tcpSocketConnected" << socket->isWritable(); // If network is on ssl, do not believe when they are connected, believe when handshake is completed NetworkPacket np2(QLatin1String("")); NetworkPacket::createIdentityPacket(&np2); socket->write(np2.serialize()); bool success = socket->waitForBytesWritten(); if (success) { qCDebug(KDECONNECT_CORE) << "TCP connection done (i'm the existing device)"; // if ssl supported if (receivedPacket->get(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) { bool isDeviceTrusted = KdeConnectConfig::instance()->trustedDevices().contains(deviceId); configureSslSocket(socket, deviceId, isDeviceTrusted); qCDebug(KDECONNECT_CORE) << "Starting server ssl (I'm the client TCP socket)"; connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted); if (isDeviceTrusted) { connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); } socket->startServerEncryption(); return; // Return statement prevents from deleting received packet, needed in slot "encrypted" } else { qWarning() << receivedPacket->get(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work"; //addLink(deviceId, socket, receivedPacket, LanDeviceLink::Remotely); } } else { //I think this will never happen, but if it happens the deviceLink //(or the socket that is now inside it) might not be valid. Delete them. qCDebug(KDECONNECT_CORE) << "Fallback (2), try reverse connection (send udp packet)"; m_udpSocket.writeDatagram(np2.serialize(), m_receivedIdentityPackets[socket].sender, m_udpBroadcastPort); } delete m_receivedIdentityPackets.take(socket).np; //We don't delete the socket because now it's owned by the LanDeviceLink } void LanLinkProvider::encrypted() { qCDebug(KDECONNECT_CORE) << "Socket successfully established an SSL connection"; QSslSocket* socket = qobject_cast(sender()); if (!socket) return; // TODO delete me? disconnect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); Q_ASSERT(socket->mode() != QSslSocket::UnencryptedMode); LanDeviceLink::ConnectionStarted connectionOrigin = (socket->mode() == QSslSocket::SslClientMode)? LanDeviceLink::Locally : LanDeviceLink::Remotely; NetworkPacket* receivedPacket = m_receivedIdentityPackets[socket].np; const QString& deviceId = receivedPacket->get(QStringLiteral("deviceId")); addLink(deviceId, socket, receivedPacket, connectionOrigin); // Copied from tcpSocketConnected slot, now delete received packet delete m_receivedIdentityPackets.take(socket).np; } void LanLinkProvider::sslErrors(const QList& errors) { QSslSocket* socket = qobject_cast(sender()); if (!socket) return; qCDebug(KDECONNECT_CORE) << "Failing due to " << errors; Device* device = Daemon::instance()->getDevice(socket->peerVerifyName()); if (device) { device->unpair(); } delete m_receivedIdentityPackets.take(socket).np; // Socket disconnects itself on ssl error and will be deleted by deleteLater slot, no need to delete manually } //I'm the new device and this is the answer to my UDP identity packet (no data received yet). They are connecting to us through TCP, and they should send an identity. void LanLinkProvider::newConnection() { qCDebug(KDECONNECT_CORE) << "LanLinkProvider newConnection"; while (m_server->hasPendingConnections()) { QSslSocket* socket = m_server->nextPendingConnection(); configureSocket(socket); //This socket is still managed by us (and child of the QTcpServer), if //it disconnects before we manage to pass it to a LanDeviceLink, it's //our responsibility to delete it. We do so with this connection. connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); connect(socket, &QIODevice::readyRead, this, &LanLinkProvider::dataReceived); } } //I'm the new device and this is the answer to my UDP identity packet (data received) void LanLinkProvider::dataReceived() { QSslSocket* socket = qobject_cast(sender()); socket->startTransaction(); const QByteArray data = socket->readLine(); qCDebug(KDECONNECT_CORE) << "LanLinkProvider received reply:" << data; NetworkPacket* np = new NetworkPacket(QLatin1String("")); bool success = NetworkPacket::unserialize(data, np); if (!success) { delete np; socket->rollbackTransaction(); return; } socket->commitTransaction(); if (np->type() != PACKET_TYPE_IDENTITY) { qCWarning(KDECONNECT_CORE) << "LanLinkProvider/newConnection: Expected identity, received " << np->type(); delete np; return; } // Needed in "encrypted" if ssl is used, similar to "tcpSocketConnected" m_receivedIdentityPackets[socket].np = np; const QString& deviceId = np->get(QStringLiteral("deviceId")); //qCDebug(KDECONNECT_CORE) << "Handshaking done (i'm the new device)"; //This socket will now be owned by the LanDeviceLink or we don't want more data to be received, forget about it disconnect(socket, &QIODevice::readyRead, this, &LanLinkProvider::dataReceived); if (np->get(QStringLiteral("protocolVersion")) >= MIN_VERSION_WITH_SSL_SUPPORT) { bool isDeviceTrusted = KdeConnectConfig::instance()->trustedDevices().contains(deviceId); configureSslSocket(socket, deviceId, isDeviceTrusted); qCDebug(KDECONNECT_CORE) << "Starting client ssl (but I'm the server TCP socket)"; connect(socket, &QSslSocket::encrypted, this, &LanLinkProvider::encrypted); if (isDeviceTrusted) { connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &LanLinkProvider::sslErrors); } socket->startClientEncryption(); } else { qWarning() << np->get(QStringLiteral("deviceName")) << "uses an old protocol version, this won't work"; //addLink(deviceId, socket, np, LanDeviceLink::Locally); delete m_receivedIdentityPackets.take(socket).np; } } void LanLinkProvider::deviceLinkDestroyed(QObject* destroyedDeviceLink) { const QString id = destroyedDeviceLink->property("deviceId").toString(); //qCDebug(KDECONNECT_CORE) << "deviceLinkDestroyed" << id; QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(id); Q_ASSERT(linkIterator != m_links.end()); if (linkIterator != m_links.end()) { Q_ASSERT(linkIterator.value() == destroyedDeviceLink); m_links.erase(linkIterator); auto pairingHandler = m_pairingHandlers.take(id); if (pairingHandler) { pairingHandler->deleteLater(); } } } void LanLinkProvider::configureSslSocket(QSslSocket* socket, const QString& deviceId, bool isDeviceTrusted) { // Setting supported ciphers manually, to match those on Android (FIXME: Test if this can be left unconfigured and still works for Android 4) QList socketCiphers; socketCiphers.append(QSslCipher(QStringLiteral("ECDHE-ECDSA-AES256-GCM-SHA384"))); socketCiphers.append(QSslCipher(QStringLiteral("ECDHE-ECDSA-AES128-GCM-SHA256"))); socketCiphers.append(QSslCipher(QStringLiteral("ECDHE-RSA-AES128-SHA"))); // Configure for ssl QSslConfiguration sslConfig; sslConfig.setCiphers(socketCiphers); socket->setSslConfiguration(sslConfig); socket->setLocalCertificate(KdeConnectConfig::instance()->certificate()); socket->setPrivateKey(KdeConnectConfig::instance()->privateKeyPath()); socket->setPeerVerifyName(deviceId); if (isDeviceTrusted) { QString certString = KdeConnectConfig::instance()->getDeviceProperty(deviceId, QStringLiteral("certificate"), QString()); socket->addCaCertificate(QSslCertificate(certString.toLatin1())); socket->setPeerVerifyMode(QSslSocket::VerifyPeer); } else { socket->setPeerVerifyMode(QSslSocket::QueryPeer); } //Usually SSL errors are only bad for trusted devices. Uncomment this section to log errors in any case, for debugging. //QObject::connect(socket, static_cast&)>(&QSslSocket::sslErrors), [](const QList& errors) //{ // Q_FOREACH (const QSslError& error, errors) { // qCDebug(KDECONNECT_CORE) << "SSL Error:" << error.errorString(); // } //}); } void LanLinkProvider::configureSocket(QSslSocket* socket) { socket->setProxy(QNetworkProxy::NoProxy); socket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1)); #ifdef TCP_KEEPIDLE // time to start sending keepalive packets (seconds) int maxIdle = 10; setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle)); #endif #ifdef TCP_KEEPINTVL // interval between keepalive packets after the initial period (seconds) int interval = 5; setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); #endif #ifdef TCP_KEEPCNT // number of missed keepalive packets before disconnecting int count = 3; setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count)); #endif } void LanLinkProvider::addLink(const QString& deviceId, QSslSocket* socket, NetworkPacket* receivedPacket, LanDeviceLink::ConnectionStarted connectionOrigin) { // Socket disconnection will now be handled by LanDeviceLink disconnect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); LanDeviceLink* deviceLink; //Do we have a link for this device already? QMap< QString, LanDeviceLink* >::iterator linkIterator = m_links.find(deviceId); if (linkIterator != m_links.end()) { //qCDebug(KDECONNECT_CORE) << "Reusing link to" << deviceId; deviceLink = linkIterator.value(); deviceLink->reset(socket, connectionOrigin); } else { deviceLink = new LanDeviceLink(deviceId, this, socket, connectionOrigin); connect(deviceLink, &QObject::destroyed, this, &LanLinkProvider::deviceLinkDestroyed); m_links[deviceId] = deviceLink; if (m_pairingHandlers.contains(deviceId)) { //We shouldn't have a pairinghandler if we didn't have a link. //Crash if debug, recover if release (by setting the new devicelink to the old pairinghandler) Q_ASSERT(m_pairingHandlers.contains(deviceId)); m_pairingHandlers[deviceId]->setDeviceLink(deviceLink); } } Q_EMIT onConnectionReceived(*receivedPacket, deviceLink); } LanPairingHandler* LanLinkProvider::createPairingHandler(DeviceLink* link) { LanPairingHandler* ph = m_pairingHandlers.value(link->deviceId()); if (!ph) { ph = new LanPairingHandler(link); qCDebug(KDECONNECT_CORE) << "creating pairing handler for" << link->deviceId(); connect (ph, &LanPairingHandler::pairingError, link, &DeviceLink::pairingError); m_pairingHandlers[link->deviceId()] = ph; } return ph; } void LanLinkProvider::userRequestsPair(const QString& deviceId) { LanPairingHandler* ph = createPairingHandler(m_links.value(deviceId)); ph->requestPairing(); } void LanLinkProvider::userRequestsUnpair(const QString& deviceId) { LanPairingHandler* ph = createPairingHandler(m_links.value(deviceId)); ph->unpair(); } void LanLinkProvider::incomingPairPacket(DeviceLink* deviceLink, const NetworkPacket& np) { LanPairingHandler* ph = createPairingHandler(deviceLink); ph->packetReceived(np); } diff --git a/core/backends/lan/lanpairinghandler.cpp b/core/backends/lan/lanpairinghandler.cpp index bac740e7..a008196a 100644 --- a/core/backends/lan/lanpairinghandler.cpp +++ b/core/backends/lan/lanpairinghandler.cpp @@ -1,143 +1,143 @@ /** * Copyright 2015 Vineet Garg * * 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 . */ #include "lanpairinghandler.h" #include #include "core_debug.h" #include "daemon.h" #include "kdeconnectconfig.h" #include "landevicelink.h" #include "networkpackettypes.h" LanPairingHandler::LanPairingHandler(DeviceLink* deviceLink) : PairingHandler(deviceLink) , m_status(NotPaired) { m_pairingTimeout.setSingleShot(true); m_pairingTimeout.setInterval(pairingTimeoutMsec()); connect(&m_pairingTimeout, &QTimer::timeout, this, &LanPairingHandler::pairingTimeout); } void LanPairingHandler::packetReceived(const NetworkPacket& np) { bool wantsPair = np.get(QStringLiteral("pair")); if (wantsPair) { if (isPairRequested()) { //We started pairing qCDebug(KDECONNECT_CORE) << "Pair answer"; setInternalPairStatus(Paired); } else { qCDebug(KDECONNECT_CORE) << "Pair request"; if (isPaired()) { //I'm already paired, but they think I'm not acceptPairing(); return; } setInternalPairStatus(RequestedByPeer); } } else { //wantsPair == false qCDebug(KDECONNECT_CORE) << "Unpair request"; if (isPairRequested()) { Q_EMIT pairingError(i18n("Canceled by other peer")); } setInternalPairStatus(NotPaired); } } bool LanPairingHandler::requestPairing() { if (m_status == Paired) { Q_EMIT pairingError(i18n("%1: Already paired", deviceLink()->name())); return false; } if (m_status == RequestedByPeer) { qCDebug(KDECONNECT_CORE) << deviceLink()->name() << ": Pairing already started by the other end, accepting their request."; return acceptPairing(); } - NetworkPacket np(PACKET_TYPE_PAIR, {{"pair", true}}); + NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), true}}); const bool success = deviceLink()->sendPacket(np); if (success) { setInternalPairStatus(Requested); } return success; } bool LanPairingHandler::acceptPairing() { - NetworkPacket np(PACKET_TYPE_PAIR, {{"pair", true}}); + NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), true}}); bool success = deviceLink()->sendPacket(np); if (success) { setInternalPairStatus(Paired); } return success; } void LanPairingHandler::rejectPairing() { - NetworkPacket np(PACKET_TYPE_PAIR, {{"pair", false}}); + NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}}); deviceLink()->sendPacket(np); setInternalPairStatus(NotPaired); } void LanPairingHandler::unpair() { - NetworkPacket np(PACKET_TYPE_PAIR, {{"pair", false}}); + NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}}); deviceLink()->sendPacket(np); setInternalPairStatus(NotPaired); } void LanPairingHandler::pairingTimeout() { - NetworkPacket np(PACKET_TYPE_PAIR, {{"pair", false}}); + NetworkPacket np(PACKET_TYPE_PAIR, {{QStringLiteral("pair"), false}}); deviceLink()->sendPacket(np); setInternalPairStatus(NotPaired); //Will emit the change as well Q_EMIT pairingError(i18n("Timed out")); } void LanPairingHandler::setInternalPairStatus(LanPairingHandler::InternalPairStatus status) { if (status == Requested || status == RequestedByPeer) { m_pairingTimeout.start(); } else { m_pairingTimeout.stop(); } if (m_status == RequestedByPeer && (status == NotPaired || status == Paired)) { Q_EMIT deviceLink()->pairingRequestExpired(this); } else if (status == RequestedByPeer) { Q_EMIT deviceLink()->pairingRequest(this); } m_status = status; if (status == Paired) { deviceLink()->setPairStatus(DeviceLink::Paired); } else { deviceLink()->setPairStatus(DeviceLink::NotPaired); } } diff --git a/core/backends/lan/server.cpp b/core/backends/lan/server.cpp index 7787b4e5..04340955 100644 --- a/core/backends/lan/server.cpp +++ b/core/backends/lan/server.cpp @@ -1,53 +1,53 @@ /** * Copyright 2015 Vineet Garg * * 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 . */ #include "server.h" #include "kdeconnectconfig.h" #include "lanlinkprovider.h" #include #include #include Server::Server(QObject * parent) :QTcpServer(parent) { connect(this, &QTcpServer::acceptError, this, &Server::errorFound); } void Server::incomingConnection(qintptr socketDescriptor) { QSslSocket* serverSocket = new QSslSocket(parent()); if (serverSocket->setSocketDescriptor(socketDescriptor)) { addPendingConnection(serverSocket); } else { - qWarning() << "setSocketDescriptor failed " + serverSocket->errorString(); + qWarning() << "setSocketDescriptor failed" << serverSocket->errorString(); delete serverSocket; } } QSslSocket* Server::nextPendingConnection() { return qobject_cast(QTcpServer::nextPendingConnection()); } void Server::errorFound(QAbstractSocket::SocketError socketError) { qDebug() << "error:" << socketError; } diff --git a/core/dbushelper.cpp b/core/dbushelper.cpp index c319eb92..20383dee 100644 --- a/core/dbushelper.cpp +++ b/core/dbushelper.cpp @@ -1,40 +1,40 @@ /** * Copyright 2014 Albert Vaca * * 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 . */ #include "dbushelper.h" namespace DbusHelper { void filterNonExportableCharacters(QString& s) { - static QRegExp regexp("[^A-Za-z0-9_]", Qt::CaseSensitive, QRegExp::Wildcard); + static QRegExp regexp(QStringLiteral("[^A-Za-z0-9_]"), Qt::CaseSensitive, QRegExp::Wildcard); s.replace(regexp,QLatin1String("_")); } QDBusConnection sessionBus() { #ifdef Q_OS_MAC return QDBusConnection::connectToBus(QStringLiteral(KDECONNECT_PRIVATE_DBUS_ADDR), QStringLiteral(KDECONNECT_PRIVATE_DBUS_NAME)); #else return QDBusConnection::sessionBus(); #endif } } diff --git a/core/device.cpp b/core/device.cpp index c745d0c7..32e23a89 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -1,564 +1,564 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "device.h" #include #include #include #include #include #include #include "core_debug.h" #include "kdeconnectplugin.h" #include "pluginloader.h" #include "backends/devicelink.h" #include "backends/lan/landevicelink.h" #include "backends/linkprovider.h" #include "networkpacket.h" #include "kdeconnectconfig.h" #include "daemon.h" #include "dbushelper.h" //In older Qt released, qAsConst isnt available #include "qtcompat_p.h" class Device::DevicePrivate { public: DevicePrivate(const QString &id) : m_deviceId(id) { } ~DevicePrivate() { qDeleteAll(m_deviceLinks); m_deviceLinks.clear(); } const QString m_deviceId; QString m_deviceName; DeviceType m_deviceType; int m_protocolVersion; QVector m_deviceLinks; QHash m_plugins; QMultiMap m_pluginsByIncomingCapability; QSet m_supportedPlugins; QSet m_allPlugins; QSet m_pairRequests; }; static void warn(const QString& info) { qWarning() << "Device pairing error" << info; } Device::Device(QObject* parent, const QString& id) : QObject(parent) , d(new Device::DevicePrivate(id)) { d->m_protocolVersion = NetworkPacket::s_protocolVersion; KdeConnectConfig::DeviceInfo info = KdeConnectConfig::instance()->getTrustedDevice(d->m_deviceId); d->m_deviceName = info.deviceName; d->m_deviceType = str2type(info.deviceType); //Register in bus DbusHelper::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors); //Assume every plugin is supported until addLink is called and we can get the actual list d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet(); d->m_supportedPlugins = d->m_allPlugins; connect(this, &Device::pairingError, this, &warn); } Device::Device(QObject* parent, const NetworkPacket& identityPacket, DeviceLink* dl) : QObject(parent) , d(new Device::DevicePrivate(identityPacket.get(QStringLiteral("deviceId")))) { d->m_deviceName = identityPacket.get(QStringLiteral("deviceName")); d->m_allPlugins = PluginLoader::instance()->getPluginList().toSet(); addLink(identityPacket, dl); //Register in bus DbusHelper::sessionBus().registerObject(dbusPath(), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors); connect(this, &Device::pairingError, this, &warn); } Device::~Device() { delete d; } QString Device::id() const { return d->m_deviceId; } QString Device::name() const { return d->m_deviceName; } QString Device::type() const { return type2str(d->m_deviceType); } bool Device::isReachable() const { return !d->m_deviceLinks.isEmpty(); } int Device::protocolVersion() { return d->m_protocolVersion; } QStringList Device::supportedPlugins() const { return d->m_supportedPlugins.toList(); } bool Device::hasPlugin(const QString& name) const { return d->m_plugins.contains(name); } QStringList Device::loadedPlugins() const { return d->m_plugins.keys(); } void Device::reloadPlugins() { QHash newPluginMap, oldPluginMap = d->m_plugins; QMultiMap newPluginsByIncomingCapability; if (isTrusted() && isReachable()) { //Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices PluginLoader* loader = PluginLoader::instance(); for (const QString& pluginName : qAsConst(d->m_supportedPlugins)) { const KPluginMetaData service = loader->getPluginInfo(pluginName); const bool pluginEnabled = isPluginEnabled(pluginName); const QSet incomingCapabilities = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedPacketType")).toSet(); if (pluginEnabled) { KdeConnectPlugin* plugin = d->m_plugins.take(pluginName); if (!plugin) { plugin = loader->instantiatePluginForDevice(pluginName, this); } Q_ASSERT(plugin); for (const QString& interface : incomingCapabilities) { newPluginsByIncomingCapability.insert(interface, plugin); } newPluginMap[pluginName] = plugin; } } } const bool differentPlugins = oldPluginMap != newPluginMap; //Erase all left plugins in the original map (meaning that we don't want //them anymore, otherwise they would have been moved to the newPluginMap) qDeleteAll(d->m_plugins); d->m_plugins = newPluginMap; d->m_pluginsByIncomingCapability = newPluginsByIncomingCapability; QDBusConnection bus = DbusHelper::sessionBus(); for (KdeConnectPlugin* plugin : qAsConst(d->m_plugins)) { //TODO: see how it works in Android (only done once, when created) plugin->connected(); const QString dbusPath = plugin->dbusPath(); if (!dbusPath.isEmpty()) { bus.registerObject(dbusPath, plugin, QDBusConnection::ExportAllProperties | QDBusConnection::ExportScriptableInvokables | QDBusConnection::ExportScriptableSignals | QDBusConnection::ExportScriptableSlots); } } if (differentPlugins) { Q_EMIT pluginsChanged(); } } QString Device::pluginsConfigFile() const { return KdeConnectConfig::instance()->deviceConfigDir(id()).absoluteFilePath(QStringLiteral("config")); } void Device::requestPair() { if (isTrusted()) { Q_EMIT pairingError(i18n("Already paired")); return; } if (!isReachable()) { Q_EMIT pairingError(i18n("Device not reachable")); return; } for (DeviceLink* dl : qAsConst(d->m_deviceLinks)) { dl->userRequestsPair(); } } void Device::unpair() { for (DeviceLink* dl : qAsConst(d->m_deviceLinks)) { dl->userRequestsUnpair(); } KdeConnectConfig::instance()->removeTrustedDevice(id()); Q_EMIT trustedChanged(false); } void Device::pairStatusChanged(DeviceLink::PairStatus status) { if (status == DeviceLink::NotPaired) { KdeConnectConfig::instance()->removeTrustedDevice(id()); for (DeviceLink* dl : qAsConst(d->m_deviceLinks)) { if (dl != sender()) { dl->setPairStatus(DeviceLink::NotPaired); } } } else { KdeConnectConfig::instance()->addTrustedDevice(id(), name(), type()); } reloadPlugins(); //Will load/unload plugins bool isTrusted = (status == DeviceLink::Paired); Q_EMIT trustedChanged(isTrusted); Q_ASSERT(isTrusted == this->isTrusted()); } static bool lessThan(DeviceLink* p1, DeviceLink* p2) { return p1->provider()->priority() > p2->provider()->priority(); } void Device::addLink(const NetworkPacket& identityPacket, DeviceLink* link) { //qCDebug(KDECONNECT_CORE) << "Adding link to" << id() << "via" << link->provider(); setName(identityPacket.get(QStringLiteral("deviceName"))); d->m_deviceType = str2type(identityPacket.get(QStringLiteral("deviceType"))); if (d->m_deviceLinks.contains(link)) return; d->m_protocolVersion = identityPacket.get(QStringLiteral("protocolVersion"), -1); if (d->m_protocolVersion != NetworkPacket::s_protocolVersion) { qCWarning(KDECONNECT_CORE) << d->m_deviceName << "- warning, device uses a different protocol version" << d->m_protocolVersion << "expected" << NetworkPacket::s_protocolVersion; } connect(link, &QObject::destroyed, this, &Device::linkDestroyed); d->m_deviceLinks.append(link); //Theoretically we will never add two links from the same provider (the provider should destroy //the old one before this is called), so we do not have to worry about destroying old links. //-- Actually, we should not destroy them or the provider will store an invalid ref! connect(link, &DeviceLink::receivedPacket, this, &Device::privateReceivedPacket); std::sort(d->m_deviceLinks.begin(), d->m_deviceLinks.end(), lessThan); const bool capabilitiesSupported = identityPacket.has(QStringLiteral("incomingCapabilities")) || identityPacket.has(QStringLiteral("outgoingCapabilities")); if (capabilitiesSupported) { const QSet outgoingCapabilities = identityPacket.get(QStringLiteral("outgoingCapabilities")).toSet() , incomingCapabilities = identityPacket.get(QStringLiteral("incomingCapabilities")).toSet(); d->m_supportedPlugins = PluginLoader::instance()->pluginsForCapabilities(incomingCapabilities, outgoingCapabilities); //qDebug() << "new plugins for" << m_deviceName << m_supportedPlugins << incomingCapabilities << outgoingCapabilities; } else { d->m_supportedPlugins = PluginLoader::instance()->getPluginList().toSet(); } reloadPlugins(); if (d->m_deviceLinks.size() == 1) { Q_EMIT reachableChanged(true); } connect(link, &DeviceLink::pairStatusChanged, this, &Device::pairStatusChanged); connect(link, &DeviceLink::pairingRequest, this, &Device::addPairingRequest); connect(link, &DeviceLink::pairingRequestExpired, this, &Device::removePairingRequest); connect(link, &DeviceLink::pairingError, this, &Device::pairingError); } void Device::addPairingRequest(PairingHandler* handler) { const bool wasEmpty = d->m_pairRequests.isEmpty(); d->m_pairRequests.insert(handler); if (wasEmpty != d->m_pairRequests.isEmpty()) Q_EMIT hasPairingRequestsChanged(!d->m_pairRequests.isEmpty()); } void Device::removePairingRequest(PairingHandler* handler) { const bool wasEmpty = d->m_pairRequests.isEmpty(); d->m_pairRequests.remove(handler); if (wasEmpty != d->m_pairRequests.isEmpty()) Q_EMIT hasPairingRequestsChanged(!d->m_pairRequests.isEmpty()); } bool Device::hasPairingRequests() const { return !d->m_pairRequests.isEmpty(); } void Device::acceptPairing() { if (d->m_pairRequests.isEmpty()) qWarning() << "no pair requests to accept!"; //copying because the pairing handler will be removed upon accept const auto prCopy = d->m_pairRequests; for (auto ph: prCopy) ph->acceptPairing(); } void Device::rejectPairing() { if (d->m_pairRequests.isEmpty()) qWarning() << "no pair requests to reject!"; //copying because the pairing handler will be removed upon reject const auto prCopy = d->m_pairRequests; for (auto ph: prCopy) ph->rejectPairing(); } void Device::linkDestroyed(QObject* o) { removeLink(static_cast(o)); } void Device::removeLink(DeviceLink* link) { d->m_deviceLinks.removeAll(link); //qCDebug(KDECONNECT_CORE) << "RemoveLink" << m_deviceLinks.size() << "links remaining"; if (d->m_deviceLinks.isEmpty()) { reloadPlugins(); Q_EMIT reachableChanged(false); } } bool Device::sendPacket(NetworkPacket& np) { Q_ASSERT(np.type() != PACKET_TYPE_PAIR); Q_ASSERT(isTrusted()); //Maybe we could block here any packet that is not an identity or a pairing packet to prevent sending non encrypted data for (DeviceLink* dl : qAsConst(d->m_deviceLinks)) { if (dl->sendPacket(np)) return true; } return false; } void Device::privateReceivedPacket(const NetworkPacket& np) { Q_ASSERT(np.type() != PACKET_TYPE_PAIR); if (isTrusted()) { const QList plugins = d->m_pluginsByIncomingCapability.values(np.type()); if (plugins.isEmpty()) { qWarning() << "discarding unsupported packet" << np.type() << "for" << name(); } for (KdeConnectPlugin* plugin : plugins) { plugin->receivePacket(np); } } else { qCDebug(KDECONNECT_CORE) << "device" << name() << "not paired, ignoring packet" << np.type(); unpair(); } } bool Device::isTrusted() const { return KdeConnectConfig::instance()->trustedDevices().contains(id()); } QStringList Device::availableLinks() const { QStringList sl; sl.reserve(d->m_deviceLinks.size()); for (DeviceLink* dl : qAsConst(d->m_deviceLinks)) { sl.append(dl->provider()->name()); } return sl; } void Device::cleanUnneededLinks() { if (isTrusted()) { return; } for(int i = 0; i < d->m_deviceLinks.size(); ) { DeviceLink* dl = d->m_deviceLinks[i]; if (!dl->linkShouldBeKeptAlive()) { dl->deleteLater(); d->m_deviceLinks.remove(i); } else { i++; } } } QHostAddress Device::getLocalIpAddress() const { for (DeviceLink* dl : qAsConst(d->m_deviceLinks)) { LanDeviceLink* ldl = dynamic_cast(dl); if (ldl) { return ldl->hostAddress(); } } return QHostAddress::Null; } Device::DeviceType Device::str2type(const QString& deviceType) { if (deviceType == QLatin1String("desktop")) return Desktop; if (deviceType == QLatin1String("laptop")) return Laptop; if (deviceType == QLatin1String("smartphone") || deviceType == QLatin1String("phone")) return Phone; if (deviceType == QLatin1String("tablet")) return Tablet; if (deviceType == QLatin1String("tv")) return Tv; return Unknown; } QString Device::type2str(Device::DeviceType deviceType) { if (deviceType == Desktop) return QStringLiteral("desktop"); if (deviceType == Laptop) return QStringLiteral("laptop"); if (deviceType == Phone) return QStringLiteral("smartphone"); if (deviceType == Tablet) return QStringLiteral("tablet"); if (deviceType == Tv) return QStringLiteral("tv"); return QStringLiteral("unknown"); } QString Device::statusIconName() const { return iconForStatus(isReachable(), isTrusted()); } QString Device::iconName() const { return iconForStatus(true, false); } QString Device::iconForStatus(bool reachable, bool trusted) const { Device::DeviceType deviceType = d->m_deviceType; if (deviceType == Device::Unknown) { deviceType = Device::Phone; //Assume phone if we don't know the type } else if (deviceType == Device::Desktop) { deviceType = Device::Device::Laptop; // We don't have desktop icon yet } QString status = (reachable? (trusted? QStringLiteral("connected") : QStringLiteral("disconnected")) : QStringLiteral("trusted")); QString type = type2str(deviceType); return type+status; } void Device::setName(const QString& name) { if (d->m_deviceName != name) { d->m_deviceName = name; Q_EMIT nameChanged(name); } } KdeConnectPlugin* Device::plugin(const QString& pluginName) const { return d->m_plugins[pluginName]; } void Device::setPluginEnabled(const QString& pluginName, bool enabled) { if (!d->m_allPlugins.contains(pluginName)) { return; } KConfigGroup pluginStates = KSharedConfig::openConfig(pluginsConfigFile())->group("Plugins"); const QString enabledKey = pluginName + QStringLiteral("Enabled"); pluginStates.writeEntry(enabledKey, enabled); reloadPlugins(); } bool Device::isPluginEnabled(const QString& pluginName) const { const QString enabledKey = pluginName + QStringLiteral("Enabled"); KConfigGroup pluginStates = KSharedConfig::openConfig(pluginsConfigFile())->group("Plugins"); return (pluginStates.hasKey(enabledKey) ? pluginStates.readEntry(enabledKey, false) : PluginLoader::instance()->getPluginInfo(pluginName).isEnabledByDefault()); } QString Device::encryptionInfo() const { QString result; QCryptographicHash::Algorithm digestAlgorithm = QCryptographicHash::Algorithm::Sha1; QString localSha1 = QString::fromLatin1(KdeConnectConfig::instance()->certificate().digest(digestAlgorithm).toHex()); for (int i = 2; igetDeviceProperty(id(), QStringLiteral("certificate")).toStdString(); QSslCertificate remoteCertificate = QSslCertificate(QByteArray(remotePem.c_str(), (int)remotePem.size())); QString remoteSha1 = QString::fromLatin1(remoteCertificate.digest(digestAlgorithm).toHex()); for (int i = 2; i < remoteSha1.size(); i += 3) { - remoteSha1.insert(i, ':'); // Improve readability + remoteSha1.insert(i, QStringLiteral(":")); // Improve readability } result += i18n("SHA1 fingerprint of remote device certificate is: %1\n", remoteSha1); return result; } QString Device::pluginIconName(const QString& pluginName) { if (hasPlugin(pluginName)) { return d->m_plugins[pluginName]->iconName(); } return QString(); } diff --git a/core/device.h b/core/device.h index 699a8b06..b148cd89 100644 --- a/core/device.h +++ b/core/device.h @@ -1,154 +1,154 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #ifndef DEVICE_H #define DEVICE_H #include #include #include #include "networkpacket.h" #include "backends/devicelink.h" class DeviceLink; class KdeConnectPlugin; class KDECONNECTCORE_EXPORT Device : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device") Q_PROPERTY(QString type READ type CONSTANT) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString iconName READ iconName CONSTANT) Q_PROPERTY(QString statusIconName READ statusIconName) Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged) Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChanged) Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged) Q_PROPERTY(bool hasPairingRequests READ hasPairingRequests NOTIFY hasPairingRequestsChanged) public: enum DeviceType { Unknown, Desktop, Laptop, Phone, Tablet, Tv, }; /** * Restores the @p device from the saved configuration * * We already know it but we need to wait for an incoming DeviceLink to communicate */ Device(QObject* parent, const QString& id); /** * Device known via an incoming connection sent to us via a devicelink. * * We know everything but we don't trust it yet */ Device(QObject* parent, const NetworkPacket& np, DeviceLink* dl); ~Device() override; QString id() const; QString name() const; - QString dbusPath() const { return "/modules/kdeconnect/devices/"+id(); } + QString dbusPath() const { return QStringLiteral("/modules/kdeconnect/devices/") + id(); } QString type() const; QString iconName() const; QString statusIconName() const; Q_SCRIPTABLE QString encryptionInfo() const; //Add and remove links void addLink(const NetworkPacket& identityPacket, DeviceLink*); void removeLink(DeviceLink*); Q_SCRIPTABLE bool isTrusted() const; Q_SCRIPTABLE QStringList availableLinks() const; virtual bool isReachable() const; Q_SCRIPTABLE QStringList loadedPlugins() const; Q_SCRIPTABLE bool hasPlugin(const QString& name) const; Q_SCRIPTABLE QString pluginsConfigFile() const; KdeConnectPlugin* plugin(const QString& pluginName) const; Q_SCRIPTABLE void setPluginEnabled(const QString& pluginName, bool enabled); Q_SCRIPTABLE bool isPluginEnabled(const QString& pluginName) const; void cleanUnneededLinks(); int protocolVersion(); QStringList supportedPlugins() const; QHostAddress getLocalIpAddress() const; public Q_SLOTS: ///sends a @p np packet to the device ///virtual for testing purposes. virtual bool sendPacket(NetworkPacket& np); //Dbus operations public Q_SLOTS: Q_SCRIPTABLE void requestPair(); //to all links Q_SCRIPTABLE void unpair(); //from all links Q_SCRIPTABLE void reloadPlugins(); //from kconf Q_SCRIPTABLE void acceptPairing(); Q_SCRIPTABLE void rejectPairing(); Q_SCRIPTABLE bool hasPairingRequests() const; Q_SCRIPTABLE QString pluginIconName(const QString& pluginName); private Q_SLOTS: void privateReceivedPacket(const NetworkPacket& np); void linkDestroyed(QObject* o); void pairStatusChanged(DeviceLink::PairStatus current); void addPairingRequest(PairingHandler* handler); void removePairingRequest(PairingHandler* handler); Q_SIGNALS: Q_SCRIPTABLE void pluginsChanged(); Q_SCRIPTABLE void reachableChanged(bool reachable); Q_SCRIPTABLE void trustedChanged(bool trusted); Q_SCRIPTABLE void pairingError(const QString& error); Q_SCRIPTABLE void nameChanged(const QString& name); Q_SCRIPTABLE void hasPairingRequestsChanged(bool hasPairingRequests); private: //Methods static DeviceType str2type(const QString& deviceType); static QString type2str(DeviceType deviceType); void setName(const QString& name); QString iconForStatus(bool reachable, bool paired) const; private: class DevicePrivate; DevicePrivate *d; }; Q_DECLARE_METATYPE(Device*) #endif // DEVICE_H diff --git a/core/kdeconnectconfig.cpp b/core/kdeconnectconfig.cpp index 8916dc90..79cde179 100644 --- a/core/kdeconnectconfig.cpp +++ b/core/kdeconnectconfig.cpp @@ -1,329 +1,329 @@ /** * Copyright 2015 Albert Vaca * * 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 . */ #include "kdeconnectconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core_debug.h" #include "dbushelper.h" #include "daemon.h" const QFile::Permissions strictPermissions = QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser; struct KdeConnectConfigPrivate { // The Initializer object sets things up, and also does cleanup when it goes out of scope // Note it's not being used anywhere. That's intended QCA::Initializer m_qcaInitializer; QCA::PrivateKey m_privateKey; QSslCertificate m_certificate; // Use QSslCertificate instead of QCA::Certificate due to compatibility with QSslSocket QSettings* m_config; QSettings* m_trustedDevices; }; KdeConnectConfig* KdeConnectConfig::instance() { static KdeConnectConfig* kcc = new KdeConnectConfig(); return kcc; } KdeConnectConfig::KdeConnectConfig() : d(new KdeConnectConfigPrivate) { //qCDebug(KDECONNECT_CORE) << "QCA supported capabilities:" << QCA::supportedFeatures().join(","); if(!QCA::isSupported("rsa")) { qCritical() << "Could not find support for RSA in your QCA installation"; Daemon::instance()->reportError( i18n("KDE Connect failed to start"), i18n("Could not find support for RSA in your QCA installation. If your " "distribution provides separate packets for QCA-ossl and QCA-gnupg, " "make sure you have them installed and try again.")); } //Make sure base directory exists QDir().mkpath(baseConfigDir().path()); //.config/kdeconnect/config d->m_config = new QSettings(baseConfigDir().absoluteFilePath(QStringLiteral("config")), QSettings::IniFormat); d->m_trustedDevices = new QSettings(baseConfigDir().absoluteFilePath(QStringLiteral("trusted_devices")), QSettings::IniFormat); loadPrivateKey(); loadCertificate(); } QString KdeConnectConfig::name() { QString username; #ifdef Q_OS_WIN - username = qgetenv("USERNAME"); + username = QString::fromLatin1(qgetenv("USERNAME")); #else - username = qgetenv("USER"); + username = QString::fromLatin1(qgetenv("USER")); #endif - QString defaultName = username + '@' + QHostInfo::localHostName(); + QString defaultName = username + QStringLiteral("@") + QHostInfo::localHostName(); QString name = d->m_config->value(QStringLiteral("name"), defaultName).toString(); return name; } void KdeConnectConfig::setName(const QString& name) { d->m_config->setValue(QStringLiteral("name"), name); d->m_config->sync(); } QString KdeConnectConfig::deviceType() { return QStringLiteral("desktop"); // TODO } QString KdeConnectConfig::deviceId() { return d->m_certificate.subjectInfo(QSslCertificate::CommonName).constFirst(); } QString KdeConnectConfig::privateKeyPath() { return baseConfigDir().absoluteFilePath(QStringLiteral("privateKey.pem")); } QString KdeConnectConfig::certificatePath() { return baseConfigDir().absoluteFilePath(QStringLiteral("certificate.pem")); } QSslCertificate KdeConnectConfig::certificate() { return d->m_certificate; } QDir KdeConnectConfig::baseConfigDir() { QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); QString kdeconnectConfigPath = QDir(configPath).absoluteFilePath(QStringLiteral("kdeconnect")); return QDir(kdeconnectConfigPath); } QStringList KdeConnectConfig::trustedDevices() { const QStringList& list = d->m_trustedDevices->childGroups(); return list; } void KdeConnectConfig::addTrustedDevice(const QString& id, const QString& name, const QString& type) { d->m_trustedDevices->beginGroup(id); d->m_trustedDevices->setValue(QStringLiteral("name"), name); d->m_trustedDevices->setValue(QStringLiteral("type"), type); d->m_trustedDevices->endGroup(); d->m_trustedDevices->sync(); QDir().mkpath(deviceConfigDir(id).path()); } KdeConnectConfig::DeviceInfo KdeConnectConfig::getTrustedDevice(const QString& id) { d->m_trustedDevices->beginGroup(id); KdeConnectConfig::DeviceInfo info; info.deviceName = d->m_trustedDevices->value(QStringLiteral("name"), QLatin1String("unnamed")).toString(); info.deviceType = d->m_trustedDevices->value(QStringLiteral("type"), QLatin1String("unknown")).toString(); d->m_trustedDevices->endGroup(); return info; } void KdeConnectConfig::removeTrustedDevice(const QString& deviceId) { d->m_trustedDevices->remove(deviceId); d->m_trustedDevices->sync(); //We do not remove the config files. } // Utility functions to set and get a value void KdeConnectConfig::setDeviceProperty(const QString& deviceId, const QString& key, const QString& value) { d->m_trustedDevices->beginGroup(deviceId); d->m_trustedDevices->setValue(key, value); d->m_trustedDevices->endGroup(); d->m_trustedDevices->sync(); } QString KdeConnectConfig::getDeviceProperty(const QString& deviceId, const QString& key, const QString& defaultValue) { QString value; d->m_trustedDevices->beginGroup(deviceId); value = d->m_trustedDevices->value(key, defaultValue).toString(); d->m_trustedDevices->endGroup(); return value; } QDir KdeConnectConfig::deviceConfigDir(const QString& deviceId) { QString deviceConfigPath = baseConfigDir().absoluteFilePath(deviceId); return QDir(deviceConfigPath); } QDir KdeConnectConfig::pluginConfigDir(const QString& deviceId, const QString& pluginName) { QString deviceConfigPath = baseConfigDir().absoluteFilePath(deviceId); QString pluginConfigDir = QDir(deviceConfigPath).absoluteFilePath(pluginName); return QDir(pluginConfigDir); } void KdeConnectConfig::loadPrivateKey() { QString keyPath = privateKeyPath(); QFile privKey(keyPath); bool needsToGenerateKey = false; if (privKey.exists() && privKey.open(QIODevice::ReadOnly)) { QCA::ConvertResult result; - d->m_privateKey = QCA::PrivateKey::fromPEM(privKey.readAll(), QCA::SecureArray(), &result); + d->m_privateKey = QCA::PrivateKey::fromPEM(QString::fromLatin1(privKey.readAll()), QCA::SecureArray(), &result); if (result != QCA::ConvertResult::ConvertGood) { qCWarning(KDECONNECT_CORE) << "Private key from" << keyPath << "is not valid"; needsToGenerateKey = true; } } else { needsToGenerateKey = true; } if (needsToGenerateKey) { generatePrivateKey(keyPath); } //Extra security check if (QFile::permissions(keyPath) != strictPermissions) { qCWarning(KDECONNECT_CORE) << "Warning: KDE Connect private key file has too open permissions " << keyPath; } } void KdeConnectConfig::loadCertificate() { QString certPath = certificatePath(); QFile cert(certPath); bool needsToGenerateCert = false; if (cert.exists() && cert.open(QIODevice::ReadOnly)) { auto loadedCerts = QSslCertificate::fromPath(certPath); if (loadedCerts.empty()) { qCWarning(KDECONNECT_CORE) << "Certificate from" << certPath << "is not valid"; needsToGenerateCert = true; } else { d->m_certificate = loadedCerts.at(0); } } else { needsToGenerateCert = true; } if (needsToGenerateCert) { generateCertificate(certPath); } //Extra security check if (QFile::permissions(certPath) != strictPermissions) { qCWarning(KDECONNECT_CORE) << "Warning: KDE Connect certificate file has too open permissions " << certPath; } } void KdeConnectConfig::generatePrivateKey(const QString& keyPath) { qCDebug(KDECONNECT_CORE) << "Generating private key"; bool error = false; d->m_privateKey = QCA::KeyGenerator().createRSA(2048); QFile privKey(keyPath); if (!privKey.open(QIODevice::ReadWrite | QIODevice::Truncate)) { error = true; } else { privKey.setPermissions(strictPermissions); int written = privKey.write(d->m_privateKey.toPEM().toLatin1()); if (written <= 0) { error = true; } } if (error) { Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store private key file: %1", keyPath)); } } void KdeConnectConfig::generateCertificate(const QString& certPath) { qCDebug(KDECONNECT_CORE) << "Generating certificate"; bool error = false; QString uuid = QUuid::createUuid().toString(); DbusHelper::filterNonExportableCharacters(uuid); qCDebug(KDECONNECT_CORE) << "My id:" << uuid; // FIXME: We only use QCA here to generate the cert and key, would be nice to get rid of it completely. // The same thing we are doing with QCA could be done invoking openssl (although it's potentially less portable): // openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes -keyout privateKey.pem -days 3650 -out certificate.pem -subj "/O=KDE/OU=KDE Connect/CN=_e6e29ad4_2b31_4b6d_8f7a_9872dbaa9095_" QCA::CertificateOptions certificateOptions = QCA::CertificateOptions(); QDateTime startTime = QDateTime::currentDateTime().addYears(-1); QDateTime endTime = startTime.addYears(10); QCA::CertificateInfo certificateInfo; certificateInfo.insert(QCA::CommonName, uuid); certificateInfo.insert(QCA::Organization,QStringLiteral("KDE")); certificateInfo.insert(QCA::OrganizationalUnit,QStringLiteral("Kde connect")); certificateOptions.setInfo(certificateInfo); certificateOptions.setFormat(QCA::PKCS10); certificateOptions.setSerialNumber(QCA::BigInteger(10)); certificateOptions.setValidityPeriod(startTime, endTime); d->m_certificate = QSslCertificate(QCA::Certificate(certificateOptions, d->m_privateKey).toPEM().toLatin1()); QFile cert(certPath); if (!cert.open(QIODevice::ReadWrite | QIODevice::Truncate)) { error = true; } else { cert.setPermissions(strictPermissions); int written = cert.write(d->m_certificate.toPem()); if (written <= 0) { error = true; } } if (error) { Daemon::instance()->reportError(QStringLiteral("KDE Connect"), i18n("Could not store certificate file: %1", certPath)); } } diff --git a/core/kdeconnectpluginconfig.cpp b/core/kdeconnectpluginconfig.cpp index 27192530..a59975e5 100644 --- a/core/kdeconnectpluginconfig.cpp +++ b/core/kdeconnectpluginconfig.cpp @@ -1,100 +1,100 @@ /** * Copyright 2015 Albert Vaca * * 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 . */ #include "kdeconnectpluginconfig.h" #include #include #include #include "kdeconnectconfig.h" #include "dbushelper.h" struct KdeConnectPluginConfigPrivate { QDir m_configDir; QSettings* m_config; QDBusMessage m_signal; }; KdeConnectPluginConfig::KdeConnectPluginConfig(const QString& deviceId, const QString& pluginName) : d(new KdeConnectPluginConfigPrivate()) { d->m_configDir = KdeConnectConfig::instance()->pluginConfigDir(deviceId, pluginName); QDir().mkpath(d->m_configDir.path()); d->m_config = new QSettings(d->m_configDir.absoluteFilePath(QStringLiteral("config")), QSettings::IniFormat); - d->m_signal = QDBusMessage::createSignal("/kdeconnect/"+deviceId+"/"+pluginName, QStringLiteral("org.kde.kdeconnect.config"), QStringLiteral("configChanged")); - DbusHelper::sessionBus().connect(QLatin1String(""), "/kdeconnect/"+deviceId+"/"+pluginName, QStringLiteral("org.kde.kdeconnect.config"), QStringLiteral("configChanged"), this, SLOT(slotConfigChanged())); + d->m_signal = QDBusMessage::createSignal(QStringLiteral("/kdeconnect/") + deviceId + QStringLiteral("/") + pluginName, QStringLiteral("org.kde.kdeconnect.config"), QStringLiteral("configChanged")); + DbusHelper::sessionBus().connect(QLatin1String(""), QStringLiteral("/kdeconnect/") + deviceId + QStringLiteral("/") + pluginName, QStringLiteral("org.kde.kdeconnect.config"), QStringLiteral("configChanged"), this, SLOT(slotConfigChanged())); } KdeConnectPluginConfig::~KdeConnectPluginConfig() { delete d->m_config; } QVariant KdeConnectPluginConfig::get(const QString& key, const QVariant& defaultValue) { d->m_config->sync(); return d->m_config->value(key, defaultValue); } QVariantList KdeConnectPluginConfig::getList(const QString& key, const QVariantList& defaultValue) { QVariantList list; d->m_config->sync(); // note: need sync() to get recent changes signalled from other process int size = d->m_config->beginReadArray(key); if (size < 1) { d->m_config->endArray(); return defaultValue; } for (int i = 0; i < size; ++i) { d->m_config->setArrayIndex(i); list << d->m_config->value(QStringLiteral("value")); } d->m_config->endArray(); return list; } void KdeConnectPluginConfig::set(const QString& key, const QVariant& value) { d->m_config->setValue(key, value); d->m_config->sync(); DbusHelper::sessionBus().send(d->m_signal); } void KdeConnectPluginConfig::setList(const QString& key, const QVariantList& list) { d->m_config->beginWriteArray(key); for (int i = 0; i < list.size(); ++i) { d->m_config->setArrayIndex(i); d->m_config->setValue(QStringLiteral("value"), list.at(i)); } d->m_config->endArray(); d->m_config->sync(); DbusHelper::sessionBus().send(d->m_signal); } void KdeConnectPluginConfig::slotConfigChanged() { Q_EMIT configChanged(); } diff --git a/declarativeplugin/responsewaiter.cpp b/declarativeplugin/responsewaiter.cpp index dca838dc..b575d648 100644 --- a/declarativeplugin/responsewaiter.cpp +++ b/declarativeplugin/responsewaiter.cpp @@ -1,135 +1,135 @@ #include "responsewaiter.h" #include #include #include #include //In older Qt released, qAsConst isnt available #include "core/qtcompat_p.h" Q_DECLARE_METATYPE(QDBusPendingReply<>) Q_DECLARE_METATYPE(QDBusPendingReply) Q_DECLARE_METATYPE(QDBusPendingReply) Q_DECLARE_METATYPE(QDBusPendingReply) Q_DECLARE_METATYPE(QDBusPendingReply) DBusResponseWaiter* DBusResponseWaiter::m_instance = nullptr; DBusResponseWaiter* DBusResponseWaiter::instance() { if (!m_instance) { m_instance = new DBusResponseWaiter(); } return m_instance; } DBusResponseWaiter::DBusResponseWaiter() : QObject() { m_registered << qRegisterMetaType >("QDBusPendingReply<>") << qRegisterMetaType >("QDBusPendingReply") << qRegisterMetaType >("QDBusPendingReply") << qRegisterMetaType >("QDBusPendingReply") << qRegisterMetaType >("QDBusPendingReply") ; } QVariant DBusResponseWaiter::waitForReply(QVariant variant) const { if (QDBusPendingCall* call = const_cast(extractPendingCall(variant))) { call->waitForFinished(); if (call->isError()) { qWarning() << "error:" << call->error(); - return QVariant("error"); + return QVariant(QStringLiteral("error")); } QDBusMessage reply = call->reply(); if (reply.arguments().count() > 0) { return reply.arguments().at(0); } } return QVariant(); } DBusAsyncResponse::DBusAsyncResponse(QObject* parent) : QObject(parent) , m_autodelete(false) { m_timeout.setSingleShot(true); m_timeout.setInterval(15000); connect(&m_timeout, &QTimer::timeout, this, &DBusAsyncResponse::onTimeout); } void DBusAsyncResponse::setPendingCall(QVariant variant) { if (QDBusPendingCall* call = const_cast(DBusResponseWaiter::instance()->extractPendingCall(variant))) { QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(*call); watcher->setProperty("pengingCallVariant", variant); connect(watcher, &QDBusPendingCallWatcher::finished, this, &DBusAsyncResponse::onCallFinished); connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); connect(&m_timeout, &QTimer::timeout, watcher, &QObject::deleteLater); m_timeout.start(); } } void DBusAsyncResponse::onCallFinished(QDBusPendingCallWatcher* watcher) { m_timeout.stop(); QVariant variant = watcher->property("pengingCallVariant"); if (QDBusPendingCall* call = const_cast(DBusResponseWaiter::instance()->extractPendingCall(variant))) { if (call->isError()) { Q_EMIT error(call->error().message()); } else { QDBusMessage reply = call->reply(); if (reply.arguments().count() > 0) { Q_EMIT success(reply.arguments().at(0)); } else { Q_EMIT success(QVariant()); } } } if (m_autodelete) { deleteLater(); } } void DBusAsyncResponse::onTimeout() { Q_EMIT error(QStringLiteral("timeout when waiting dbus response!")); } const QDBusPendingCall* DBusResponseWaiter::extractPendingCall(QVariant& variant) const { for (int type : qAsConst(m_registered)) { if (variant.canConvert(QVariant::Type(type))) { return reinterpret_cast(variant.constData()); } } return nullptr; } diff --git a/fileitemactionplugin/sendfileitemaction.cpp b/fileitemactionplugin/sendfileitemaction.cpp index 68c17610..61947597 100644 --- a/fileitemactionplugin/sendfileitemaction.cpp +++ b/fileitemactionplugin/sendfileitemaction.cpp @@ -1,101 +1,101 @@ /* * Copyright (C) 2011 Alejandro Fiestas Olivares * Copyright (C) 2014 Aleix Pol Gonzalez * * 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) any later version. * * 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 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 "sendfileitemaction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(SendFileItemActionFactory, registerPlugin();) Q_LOGGING_CATEGORY(KDECONNECT_FILEITEMACTION, "kdeconnect.fileitemaction") SendFileItemAction::SendFileItemAction(QObject* parent, const QVariantList& ): KAbstractFileItemActionPlugin(parent) { } QList SendFileItemAction::actions(const KFileItemListProperties& fileItemInfos, QWidget* parentWidget) { QList actions; DaemonDbusInterface iface; if (!iface.isValid()) { return actions; } QDBusPendingReply reply = iface.devices(true, true); reply.waitForFinished(); const QStringList devices = reply.value(); for (const QString& id : devices) { DeviceDbusInterface deviceIface(id); if (!deviceIface.isValid()) { continue; } if (!deviceIface.hasPlugin(QStringLiteral("kdeconnect_share"))) { continue; } QAction* action = new QAction(QIcon::fromTheme(deviceIface.iconName()), deviceIface.name(), parentWidget); action->setProperty("id", id); action->setProperty("urls", QVariant::fromValue(fileItemInfos.urlList())); action->setProperty("parentWidget", QVariant::fromValue(parentWidget)); connect(action, &QAction::triggered, this, &SendFileItemAction::sendFile); actions += action; } if (actions.count() > 1) { QAction* menuAction = new QAction(QIcon::fromTheme(QStringLiteral("kdeconnect")), i18n("Send via KDE Connect"), parentWidget); QMenu* menu = new QMenu(parentWidget); menu->addActions(actions); menuAction->setMenu(menu); return QList() << menuAction; } else { if(actions.count() == 1) { actions.first()->setText(i18n("Send to '%1' via KDE Connect", actions.first()->text())); } return actions; } } void SendFileItemAction::sendFile() { const QList urls = sender()->property("urls").value>(); QString id = sender()->property("id").toString(); for (const QUrl& url : urls) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+id+"/share", QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareUrl")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + id + QStringLiteral("/share"), QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareUrl")); msg.setArguments(QVariantList() << url.toString()); DbusHelper::sessionBus().call(msg); } } #include "sendfileitemaction.moc" diff --git a/indicator/deviceindicator.cpp b/indicator/deviceindicator.cpp index d2463d9b..53ff32be 100644 --- a/indicator/deviceindicator.cpp +++ b/indicator/deviceindicator.cpp @@ -1,115 +1,115 @@ /* * Copyright 2016 Aleix Pol Gonzalez * * 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 . */ #include "deviceindicator.h" #include #include #include "interfaces/dbusinterfaces.h" #include class BatteryAction : public QAction { Q_OBJECT public: BatteryAction(DeviceDbusInterface* device) : QAction(nullptr) , m_batteryIface(new DeviceBatteryDbusInterface(device->id(), this)) { setWhenAvailable(m_batteryIface->charge(), [this](int charge) { setCharge(charge); }, this); setWhenAvailable(m_batteryIface->isCharging(), [this](bool charging) { setCharging(charging); }, this); connect(m_batteryIface, SIGNAL(chargeChanged(int)), this, SLOT(setCharge(int))); connect(m_batteryIface, SIGNAL(stateChanged(bool)), this, SLOT(setCharging(bool))); setIcon(QIcon::fromTheme(QStringLiteral("battery"))); update(); } void update() { if (m_charge < 0) setText(i18n("No Battery")); else if (m_charging) setText(i18n("Battery: %1% (Charging)", m_charge)); else setText(i18n("Battery: %1%", m_charge)); } private Q_SLOTS: void setCharge(int charge) { m_charge = charge; update(); } void setCharging(bool charging) { m_charging = charging; update(); } private: DeviceBatteryDbusInterface* m_batteryIface; int m_charge = -1; bool m_charging = false; }; DeviceIndicator::DeviceIndicator(DeviceDbusInterface* device) : QMenu(device->name(), nullptr) , m_device(device) { #ifdef Q_OS_WIN - setIcon(QIcon(QStandardPaths::locate(QStandardPaths::AppDataLocation, "icons/hicolor/scalable/status/"+device->iconName()+".svg"))); + setIcon(QIcon(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("icons/hicolor/scalable/status/") + device->iconName() + QStringLiteral(".svg")))); #else setIcon(QIcon::fromTheme(device->iconName())); #endif connect(device, SIGNAL(nameChanged(QString)), this, SLOT(setText(QString))); auto battery = new BatteryAction(device); addAction(battery); - setWhenAvailable(device->hasPlugin("kdeconnect_battery"), + setWhenAvailable(device->hasPlugin(QStringLiteral("kdeconnect_battery")), [battery](bool available) { battery->setVisible(available); } , this); auto browse = addAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("Browse device")); connect(browse, &QAction::triggered, device, [device](){ SftpDbusInterface* sftpIface = new SftpDbusInterface(device->id(), device); sftpIface->startBrowsing(); sftpIface->deleteLater(); }); - setWhenAvailable(device->hasPlugin("kdeconnect_sftp"), [browse](bool available) { browse->setVisible(available); }, this); + setWhenAvailable(device->hasPlugin(QStringLiteral("kdeconnect_sftp")), [browse](bool available) { browse->setVisible(available); }, this); auto findDevice = addAction(QIcon::fromTheme(QStringLiteral("irc-voice")), i18n("Ring device")); connect(findDevice, &QAction::triggered, device, [device](){ FindMyPhoneDeviceDbusInterface* iface = new FindMyPhoneDeviceDbusInterface(device->id(), device); iface->ring(); iface->deleteLater(); }); - setWhenAvailable(device->hasPlugin("kdeconnect_findmyphone"), [findDevice](bool available) { findDevice->setVisible(available); }, this); + setWhenAvailable(device->hasPlugin(QStringLiteral("kdeconnect_findmyphone")), [findDevice](bool available) { findDevice->setVisible(available); }, this); auto sendFile = addAction(QIcon::fromTheme(QStringLiteral("document-share")), i18n("Send file")); connect(sendFile, &QAction::triggered, device, [device, this](){ const QUrl url = QFileDialog::getOpenFileUrl(parentWidget(), i18n("Select file to send to '%1'", device->name()), QUrl::fromLocalFile(QDir::homePath())); if (url.isEmpty()) return; - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device->id()+"/share", QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareUrl")); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device->id() + QStringLiteral("/share"), QStringLiteral("org.kde.kdeconnect.device.share"), QStringLiteral("shareUrl")); msg.setArguments(QVariantList() << url.toString()); DbusHelper::sessionBus().call(msg); }); - setWhenAvailable(device->hasPlugin("kdeconnect_share"), [sendFile](bool available) { sendFile->setVisible(available); }, this); + setWhenAvailable(device->hasPlugin(QStringLiteral("kdeconnect_share")), [sendFile](bool available) { sendFile->setVisible(available); }, this); } #include "deviceindicator.moc" diff --git a/indicator/main.cpp b/indicator/main.cpp index 27cb8a24..b95f15d0 100644 --- a/indicator/main.cpp +++ b/indicator/main.cpp @@ -1,150 +1,150 @@ /* * Copyright 2016 Aleix Pol Gonzalez * * 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 . */ #include #include #ifdef QSYSTRAY #include #else #include #endif #include #include #include #include #include "interfaces/devicesmodel.h" #include "interfaces/dbusinterfaces.h" #include "kdeconnect-version.h" #include "deviceindicator.h" #ifdef Q_OS_MAC #include #endif #include int main(int argc, char** argv) { QApplication app(argc, argv); - KAboutData about("kdeconnect-indicator", + KAboutData about(QStringLiteral("kdeconnect-indicator"), i18n("KDE Connect Indicator"), QStringLiteral(KDECONNECT_VERSION_STRING), i18n("KDE Connect Indicator tool"), KAboutLicense::GPL, i18n("(C) 2016 Aleix Pol Gonzalez")); KAboutData::setApplicationData(about); #ifdef Q_OS_WIN - QProcess::startDetached("kdeconnectd.exe"); + QProcess::startDetached(QStringLiteral("kdeconnectd.exe")); #endif #ifdef Q_OS_MAC // Get bundle path CFURLRef url = (CFURLRef)CFAutorelease((CFURLRef)CFBundleCopyBundleURL(CFBundleGetMainBundle())); QString basePath = QUrl::fromCFURL(url).path(); QProcess kdeconnectdProcess; kdeconnectdProcess.start(basePath + QStringLiteral("Contents/MacOS/kdeconnectd")); // Start kdeconnectd #endif #ifndef Q_OS_MAC KDBusService dbusService(KDBusService::Unique); #endif DevicesModel model; model.setDisplayFilter(DevicesModel::Reachable | DevicesModel::Paired); QMenu* menu = new QMenu; DaemonDbusInterface iface; auto refreshMenu = [&iface, &model, &menu]() { menu->clear(); auto configure = menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure...")); QObject::connect(configure, &QAction::triggered, configure, [](){ KCMultiDialog* dialog = new KCMultiDialog; - dialog->addModule("kcm_kdeconnect"); + dialog->addModule(QStringLiteral("kcm_kdeconnect")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); }); for (int i=0, count = model.rowCount(); iaddMenu(indicator); } const QStringList requests = iface.pairingRequests(); if (!requests.isEmpty()) { menu->addSection(i18n("Pairing requests")); for(const auto& req: requests) { DeviceDbusInterface* dev = new DeviceDbusInterface(req, menu); auto pairMenu = menu->addMenu(dev->name()); pairMenu->addAction(i18n("Pair"), dev, &DeviceDbusInterface::acceptPairing); pairMenu->addAction(i18n("Reject"), dev, &DeviceDbusInterface::rejectPairing); } } #ifdef Q_OS_MAC // Add quit menu menu->addAction(i18n("Quit"), [](){ QCoreApplication::quit(); // Close this application }); #endif }; QObject::connect(&iface, &DaemonDbusInterface::pairingRequestsChangedProxy, &model, refreshMenu); QObject::connect(&model, &DevicesModel::rowsInserted, &model, refreshMenu); QObject::connect(&model, &DevicesModel::rowsRemoved, &model, refreshMenu); #ifdef QSYSTRAY QSystemTrayIcon systray; systray.setIcon(QIcon::fromTheme("kdeconnectindicatordark")); systray.setVisible(true); systray.setToolTip("KDE Connect"); QObject::connect(&model, &DevicesModel::rowsChanged, &model, [&systray, &model]() { systray.setToolTip(i18np("%1 device connected", "%1 devices connected", model.rowCount())); }); systray.setContextMenu(menu); #else KStatusNotifierItem systray; systray.setIconByName(QStringLiteral("kdeconnectindicatordark")); - systray.setToolTip(QStringLiteral("kdeconnect"), "KDE Connect", "KDE Connect"); + systray.setToolTip(QStringLiteral("kdeconnect"), QStringLiteral("KDE Connect"), QStringLiteral("KDE Connect")); systray.setCategory(KStatusNotifierItem::Communications); systray.setStatus(KStatusNotifierItem::Passive); systray.setStandardActionsEnabled(false); QObject::connect(&model, &DevicesModel::rowsChanged, &model, [&systray, &model]() { const auto count = model.rowCount(); systray.setStatus(count == 0 ? KStatusNotifierItem::Passive : KStatusNotifierItem::Active); systray.setToolTip(QStringLiteral("kdeconnect"), QStringLiteral("KDE Connect"), i18np("%1 device connected", "%1 devices connected", count)); }); systray.setContextMenu(menu); #endif refreshMenu(); app.setQuitOnLastWindowClosed(false); return app.exec(); } diff --git a/interfaces/conversationmessage.cpp b/interfaces/conversationmessage.cpp index 6d8e4884..b37bce1a 100644 --- a/interfaces/conversationmessage.cpp +++ b/interfaces/conversationmessage.cpp @@ -1,141 +1,141 @@ /** * Copyright 2018 Simon Redman * * 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 . */ #include "conversationmessage.h" #include ConversationMessage::ConversationMessage(const QVariantMap& args) - : m_eventField(args["event"].toInt()), - m_body(args["body"].toString()), - m_address(args["address"].toString()), - m_date(args["date"].toLongLong()), - m_type(args["type"].toInt()), - m_read(args["read"].toInt()), - m_threadID(args["thread_id"].toLongLong()), - m_uID(args["_id"].toInt()) + : m_eventField(args[QStringLiteral("event")].toInt()), + m_body(args[QStringLiteral("body")].toString()), + m_address(args[QStringLiteral("address")].toString()), + m_date(args[QStringLiteral("date")].toLongLong()), + m_type(args[QStringLiteral("type")].toInt()), + m_read(args[QStringLiteral("read")].toInt()), + m_threadID(args[QStringLiteral("thread_id")].toLongLong()), + m_uID(args[QStringLiteral("_id")].toInt()) { } ConversationMessage::ConversationMessage (const qint32& eventField, const QString& body, const QString& address, const qint64& date, const qint32& type, const qint32& read, const qint64& threadID, const qint32& uID) : m_eventField(eventField) , m_body(body) , m_address(address) , m_date(date) , m_type(type) , m_read(read) , m_threadID(threadID) , m_uID(uID) { } ConversationMessage::ConversationMessage(const ConversationMessage& other) : m_eventField(other.m_eventField) , m_body(other.m_body) , m_address(other.m_address) , m_date(other.m_date) , m_type(other.m_type) , m_read(other.m_read) , m_threadID(other.m_threadID) , m_uID(other.m_uID) { } ConversationMessage::~ConversationMessage() { } ConversationMessage& ConversationMessage::operator=(const ConversationMessage& other) { this->m_eventField = other.m_eventField; this->m_body = other.m_body; this->m_address = other.m_address; this->m_date = other.m_date; this->m_type = other.m_type; this->m_read = other.m_read; this->m_threadID = other.m_threadID; this->m_uID = other.m_uID; return *this; } QVariantMap ConversationMessage::toVariant() const { return { - {"event", m_eventField}, - {"body", m_body}, - {"address", m_address}, - {"date", m_date}, - {"type", m_type}, - {"read", m_read}, - {"thread_id", m_threadID}, - {"_id", m_uID}, + {QStringLiteral("event"), m_eventField}, + {QStringLiteral("body"), m_body}, + {QStringLiteral("address"), m_address}, + {QStringLiteral("date"), m_date}, + {QStringLiteral("type"), m_type}, + {QStringLiteral("read"), m_read}, + {QStringLiteral("thread_id"), m_threadID}, + {QStringLiteral("_id"), m_uID}, }; } QDBusArgument &operator<<(QDBusArgument &argument, const ConversationMessage &message) { argument.beginStructure(); argument << message.eventField() << message.body() << message.address() << message.date() << message.type() << message.read() << message.threadID() << message.uID(); argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, ConversationMessage &message) { qint32 event; QString body; QString address; qint64 date; qint32 type; qint32 read; qint64 threadID; qint32 uID; argument.beginStructure(); argument >> event; argument >> body; argument >> address; argument >> date; argument >> type; argument >> read; argument >> threadID; argument >> uID; argument.endStructure(); message = ConversationMessage(event, body, address, date, type, read, threadID, uID); return argument; } void ConversationMessage::registerDbusType() { qDBusRegisterMetaType(); qRegisterMetaType(); } diff --git a/interfaces/dbusinterfaces.cpp b/interfaces/dbusinterfaces.cpp index 31e9c21c..02f2c22e 100644 --- a/interfaces/dbusinterfaces.cpp +++ b/interfaces/dbusinterfaces.cpp @@ -1,197 +1,197 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "dbusinterfaces.h" #include QString DaemonDbusInterface::activatedService() { static const QString service = QStringLiteral("org.kde.kdeconnect"); auto reply = DbusHelper::sessionBus().interface()->startService(service); if (!reply.isValid()) { qWarning() << "error activating kdeconnectd:" << DbusHelper::sessionBus().interface()->lastError(); } return service; } DaemonDbusInterface::DaemonDbusInterface(QObject* parent) : OrgKdeKdeconnectDaemonInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect"), DbusHelper::sessionBus(), parent) { connect(this, &OrgKdeKdeconnectDaemonInterface::pairingRequestsChanged, this, &DaemonDbusInterface::pairingRequestsChangedProxy); } DaemonDbusInterface::~DaemonDbusInterface() { } DeviceDbusInterface::DeviceDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/"+id, DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") +id, DbusHelper::sessionBus(), parent) , m_id(id) { connect(this, &OrgKdeKdeconnectDeviceInterface::trustedChanged, this, &DeviceDbusInterface::trustedChangedProxy); connect(this, &OrgKdeKdeconnectDeviceInterface::reachableChanged, this, &DeviceDbusInterface::reachableChangedProxy); connect(this, &OrgKdeKdeconnectDeviceInterface::nameChanged, this, &DeviceDbusInterface::nameChangedProxy); connect(this, &OrgKdeKdeconnectDeviceInterface::hasPairingRequestsChanged, this, &DeviceDbusInterface::hasPairingRequestsChangedProxy); } DeviceDbusInterface::~DeviceDbusInterface() { } QString DeviceDbusInterface::id() const { return m_id; } void DeviceDbusInterface::pluginCall(const QString& plugin, const QString& method) { - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+id()+'/'+plugin, "org.kde.kdeconnect.device."+plugin, method); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") +id() + QStringLiteral("/") + plugin, QStringLiteral("org.kde.kdeconnect.device.") + plugin, method); DbusHelper::sessionBus().asyncCall(msg); } DeviceBatteryDbusInterface::DeviceBatteryDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceBatteryInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/"+id, DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceBatteryInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") +id, DbusHelper::sessionBus(), parent) { } DeviceBatteryDbusInterface::~DeviceBatteryDbusInterface() { } DeviceNotificationsDbusInterface::DeviceNotificationsDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceNotificationsInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/"+id, DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceNotificationsInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") +id, DbusHelper::sessionBus(), parent) { } DeviceNotificationsDbusInterface::~DeviceNotificationsDbusInterface() { } NotificationDbusInterface::NotificationDbusInterface(const QString& deviceId, const QString& notificationId, QObject* parent) - : OrgKdeKdeconnectDeviceNotificationsNotificationInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/"+deviceId+"/notifications/"+notificationId, DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceNotificationsNotificationInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/notifications/") + notificationId, DbusHelper::sessionBus(), parent) , id(notificationId) { } NotificationDbusInterface::~NotificationDbusInterface() { } DeviceConversationsDbusInterface::DeviceConversationsDbusInterface(const QString& deviceId, QObject* parent) - : OrgKdeKdeconnectDeviceConversationsInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/"+deviceId, DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceConversationsInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId, DbusHelper::sessionBus(), parent) { } DeviceConversationsDbusInterface::~DeviceConversationsDbusInterface() { } SftpDbusInterface::SftpDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceSftpInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + id + "/sftp", DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceSftpInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + id + QStringLiteral("/sftp"), DbusHelper::sessionBus(), parent) { } SftpDbusInterface::~SftpDbusInterface() { } MprisDbusInterface::MprisDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceMprisremoteInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + id + "/mprisremote", DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceMprisremoteInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + id + QStringLiteral("/mprisremote"), DbusHelper::sessionBus(), parent) { connect(this, &OrgKdeKdeconnectDeviceMprisremoteInterface::propertiesChanged, this, &MprisDbusInterface::propertiesChangedProxy); } MprisDbusInterface::~MprisDbusInterface() { } RemoteControlDbusInterface::RemoteControlDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceRemotecontrolInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + id + "/remotecontrol", DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceRemotecontrolInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + id + QStringLiteral("/remotecontrol"), DbusHelper::sessionBus(), parent) { } RemoteControlDbusInterface::~RemoteControlDbusInterface() { } LockDeviceDbusInterface::LockDeviceDbusInterface(const QString& id, QObject* parent) - : OrgKdeKdeconnectDeviceLockdeviceInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + id + "/lockdevice", DbusHelper::sessionBus(), parent) + : OrgKdeKdeconnectDeviceLockdeviceInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + id + QStringLiteral("/lockdevice"), DbusHelper::sessionBus(), parent) { connect(this, &OrgKdeKdeconnectDeviceLockdeviceInterface::lockedChanged, this, &LockDeviceDbusInterface::lockedChangedProxy); Q_ASSERT(isValid()); } LockDeviceDbusInterface::~LockDeviceDbusInterface() { } FindMyPhoneDeviceDbusInterface::FindMyPhoneDeviceDbusInterface(const QString& deviceId, QObject* parent): - OrgKdeKdeconnectDeviceFindmyphoneInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/findmyphone", DbusHelper::sessionBus(), parent) + OrgKdeKdeconnectDeviceFindmyphoneInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/findmyphone"), DbusHelper::sessionBus(), parent) { } FindMyPhoneDeviceDbusInterface::~FindMyPhoneDeviceDbusInterface() { } RemoteCommandsDbusInterface::RemoteCommandsDbusInterface(const QString& deviceId, QObject* parent): - OrgKdeKdeconnectDeviceRemotecommandsInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/remotecommands", DbusHelper::sessionBus(), parent) + OrgKdeKdeconnectDeviceRemotecommandsInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/remotecommands"), DbusHelper::sessionBus(), parent) { } RemoteCommandsDbusInterface::~RemoteCommandsDbusInterface() = default; RemoteKeyboardDbusInterface::RemoteKeyboardDbusInterface(const QString& deviceId, QObject* parent): - OrgKdeKdeconnectDeviceRemotekeyboardInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/remotekeyboard", DbusHelper::sessionBus(), parent) + OrgKdeKdeconnectDeviceRemotekeyboardInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/remotekeyboard"), DbusHelper::sessionBus(), parent) { connect(this, &OrgKdeKdeconnectDeviceRemotekeyboardInterface::remoteStateChanged, this, &RemoteKeyboardDbusInterface::remoteStateChanged); } RemoteKeyboardDbusInterface::~RemoteKeyboardDbusInterface() = default; SmsDbusInterface::SmsDbusInterface(const QString& deviceId, QObject* parent): - OrgKdeKdeconnectDeviceSmsInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/sms", DbusHelper::sessionBus(), parent) + OrgKdeKdeconnectDeviceSmsInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/sms"), DbusHelper::sessionBus(), parent) { } SmsDbusInterface::~SmsDbusInterface() = default; ShareDbusInterface::ShareDbusInterface(const QString& deviceId, QObject* parent): - OrgKdeKdeconnectDeviceShareInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/share", DbusHelper::sessionBus(), parent) + OrgKdeKdeconnectDeviceShareInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/share"), DbusHelper::sessionBus(), parent) { } ShareDbusInterface::~ShareDbusInterface() = default; RemoteSystemVolumeDbusInterface::RemoteSystemVolumeDbusInterface(const QString& deviceId, QObject* parent): - OrgKdeKdeconnectDeviceRemotesystemvolumeInterface(DaemonDbusInterface::activatedService(), "/modules/kdeconnect/devices/" + deviceId + "/remotesystemvolume", DbusHelper::sessionBus(), parent) + OrgKdeKdeconnectDeviceRemotesystemvolumeInterface(DaemonDbusInterface::activatedService(), QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/remotesystemvolume"), DbusHelper::sessionBus(), parent) { } diff --git a/kcm/main.cpp b/kcm/main.cpp index 87b6d341..4c487ebd 100644 --- a/kcm/main.cpp +++ b/kcm/main.cpp @@ -1,48 +1,48 @@ /* * Copyright 2018 Nicolas Fella * * 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 . */ #include #include #include #include #include "kdeconnect-version.h" int main(int argc, char** argv) { QApplication app(argc, argv); - KAboutData about("kdeconnect-settings", + KAboutData about(QStringLiteral("kdeconnect-settings"), i18n("KDE Connect Settings"), QStringLiteral(KDECONNECT_VERSION_STRING), i18n("KDE Connect Settings"), KAboutLicense::GPL, i18n("(C) 2018 Nicolas Fella")); KAboutData::setApplicationData(about); KCMultiDialog* dialog = new KCMultiDialog; - dialog->addModule("kcm_kdeconnect"); + dialog->addModule(QStringLiteral("kcm_kdeconnect")); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); app.setQuitOnLastWindowClosed(true); return app.exec(); } diff --git a/kio/kiokdeconnect.cpp b/kio/kiokdeconnect.cpp index c381d69e..5be080eb 100644 --- a/kio/kiokdeconnect.cpp +++ b/kio/kiokdeconnect.cpp @@ -1,276 +1,276 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "kiokdeconnect.h" #include #include #include #include #include Q_LOGGING_CATEGORY(KDECONNECT_KIO, "kdeconnect.kio") class KIOPluginForMetaData : public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kde.kio.slave.kdeconnect" FILE "kdeconnect.json") }; extern "C" int Q_DECL_EXPORT kdemain(int argc, char** argv) { QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kio_kdeconnect")); if (argc != 4) { fprintf(stderr, "Usage: kio_kdeconnect protocol pool app\n"); exit(-1); } KioKdeconnect slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } //Some useful error mapping KIO::Error toKioError(const QDBusError::ErrorType type) { switch (type) { case QDBusError::NoError: return KIO::Error(KJob::NoError); case QDBusError::NoMemory: return KIO::ERR_OUT_OF_MEMORY; case QDBusError::Timeout: return KIO::ERR_SERVER_TIMEOUT; case QDBusError::TimedOut: return KIO::ERR_SERVER_TIMEOUT; default: return KIO::ERR_SLAVE_DEFINED; }; }; template bool handleDBusError(QDBusReply& reply, KIO::SlaveBase* slave) { if (!reply.isValid()) { qCDebug(KDECONNECT_KIO) << "Error in DBus request:" << reply.error(); slave->error(toKioError(reply.error().type()),reply.error().message()); return true; } return false; } KioKdeconnect::KioKdeconnect(const QByteArray& pool, const QByteArray& app) : SlaveBase("kdeconnect", pool, app), m_dbusInterface(new DaemonDbusInterface(this)) { } void KioKdeconnect::listAllDevices() { infoMessage(i18n("Listing devices...")); //TODO: Change to all devices and show different icons for connected and disconnected? const QStringList devices = m_dbusInterface->devices(true, true); for (const QString& deviceId : devices) { DeviceDbusInterface interface(deviceId); if (!interface.hasPlugin(QStringLiteral("kdeconnect_sftp"))) continue; - const QString path = QStringLiteral("kdeconnect://").append(deviceId).append("/"); + const QString path = QStringLiteral("kdeconnect://").append(deviceId).append(QStringLiteral("/")); const QString name = interface.name(); const QString icon = QStringLiteral("kdeconnect"); KIO::UDSEntry entry; entry.reserve(6); entry.insert(KIO::UDSEntry::UDS_NAME, name); entry.insert(KIO::UDSEntry::UDS_ICON_NAME, icon); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String("")); entry.insert(KIO::UDSEntry::UDS_URL, path); listEntry(entry); } // We also need a non-null and writable UDSentry for "." KIO::UDSEntry entry; entry.reserve(4); entry.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_SIZE, 0); entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); listEntry(entry); infoMessage(QLatin1String("")); finished(); } void KioKdeconnect::listDevice(const QString& device) { infoMessage(i18n("Accessing device...")); qCDebug(KDECONNECT_KIO) << "ListDevice" << device; SftpDbusInterface interface(device); QDBusReply mountreply = interface.mountAndWait(); if (mountreply.error().type() == QDBusError::UnknownObject) { DaemonDbusInterface daemon; auto devsRepl = daemon.devices(false, false); devsRepl.waitForFinished(); if (!devsRepl.value().contains(device)) { error(KIO::ERR_SLAVE_DEFINED, i18n("No such device: %0").arg(device)); return; } DeviceDbusInterface dev(device); if (!dev.isTrusted()) { error(KIO::ERR_SLAVE_DEFINED, i18n("%0 is not paired").arg(dev.name())); return; } if (!dev.isReachable()) { error(KIO::ERR_SLAVE_DEFINED, i18n("%0 is not connected").arg(dev.name())); return; } if (!dev.hasPlugin(QStringLiteral("kdeconnect_sftp"))) { error(KIO::ERR_SLAVE_DEFINED, i18n("%0 has no Remote Filesystem plugin").arg(dev.name())); return; } } if (handleDBusError(mountreply, this)) { return; } if (!mountreply.value()) { error(KIO::ERR_SLAVE_DEFINED, interface.getMountError()); return; } QDBusReply< QVariantMap > urlreply = interface.getDirectories(); if (handleDBusError(urlreply, this)) { return; } QVariantMap urls = urlreply.value(); for (QVariantMap::iterator it = urls.begin(); it != urls.end(); ++it) { const QString path = it.key(); const QString name = it.value().toString(); const QString icon = QStringLiteral("folder"); KIO::UDSEntry entry; entry.reserve(6); entry.insert(KIO::UDSEntry::UDS_NAME, name); entry.insert(KIO::UDSEntry::UDS_ICON_NAME, icon); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QLatin1String("")); entry.insert(KIO::UDSEntry::UDS_URL, QUrl::fromLocalFile(path).toString()); listEntry(entry); } // We also need a non-null and writable UDSentry for "." KIO::UDSEntry entry; entry.reserve(4); entry.insert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_SIZE, 0); entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); listEntry(entry); infoMessage(QLatin1String("")); finished(); } void KioKdeconnect::listDir(const QUrl& url) { qCDebug(KDECONNECT_KIO) << "Listing..." << url; if (!m_dbusInterface->isValid()) { infoMessage(i18n("Could not contact background service.")); finished(); return; } QString currentDevice = url.host(); if (currentDevice.isEmpty()) { listAllDevices(); } else { listDevice(currentDevice); } } void KioKdeconnect::stat(const QUrl& url) { qCDebug(KDECONNECT_KIO) << "Stat: " << url; KIO::UDSEntry entry; entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); QString currentDevice = url.host(); if (!currentDevice.isEmpty()) { SftpDbusInterface interface(currentDevice); if (interface.isValid()) { entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, interface.mountPoint()); if (!interface.isMounted()) { interface.mount(); } } } statEntry(entry); finished(); } void KioKdeconnect::get(const QUrl& url) { qCDebug(KDECONNECT_KIO) << "Get: " << url; mimeType(QLatin1String("")); finished(); } //needed for JSON file embedding #include "kiokdeconnect.moc" diff --git a/plugins/battery/batteryplugin.cpp b/plugins/battery/batteryplugin.cpp index 5d46db85..fa214e43 100644 --- a/plugins/battery/batteryplugin.cpp +++ b/plugins/battery/batteryplugin.cpp @@ -1,81 +1,81 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "batteryplugin.h" #include #include #include #include "batterydbusinterface.h" K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_battery.json", registerPlugin< BatteryPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_BATTERY, "kdeconnect.plugin.battery") BatteryPlugin::BatteryPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , batteryDbusInterface(new BatteryDbusInterface(device())) { //TODO: Add battery reporting, could be based on: // https://www.linux-apps.com/content/show.php/battery+plasmoid+with+remaining+time?content=120309 } void BatteryPlugin::connected() { - NetworkPacket np(PACKET_TYPE_BATTERY_REQUEST, {{"request",true}}); + NetworkPacket np(PACKET_TYPE_BATTERY_REQUEST, {{QStringLiteral("request"),true}}); sendPacket(np); } BatteryPlugin::~BatteryPlugin() { //FIXME: Qt dbus does not allow to remove an adaptor! (it causes a crash in // the next dbus access to its parent). The implication of not deleting this // is that disabling the plugin does not remove the interface (that will // return outdated values) and that enabling it again instantiates a second // adaptor. This is also a memory leak until the entire device is destroyed. //batteryDbusInterface->deleteLater(); } bool BatteryPlugin::receivePacket(const NetworkPacket& np) { bool isCharging = np.get(QStringLiteral("isCharging"), false); int currentCharge = np.get(QStringLiteral("currentCharge"), -1); int thresholdEvent = np.get(QStringLiteral("thresholdEvent"), (int)ThresholdNone); if (batteryDbusInterface->charge() != currentCharge || batteryDbusInterface->isCharging() != isCharging ) { batteryDbusInterface->updateValues(isCharging, currentCharge); } if ( thresholdEvent == ThresholdBatteryLow && !isCharging ) { Daemon::instance()->sendSimpleNotification(QStringLiteral("batteryLow"), i18nc("device name: low battery", "%1: Low Battery", device()->name()), i18n("Battery at %1%", currentCharge), QStringLiteral("battery-040")); } return true; } #include "batteryplugin.moc" diff --git a/plugins/clipboard/clipboardplugin.cpp b/plugins/clipboard/clipboardplugin.cpp index 76414320..edb37955 100644 --- a/plugins/clipboard/clipboardplugin.cpp +++ b/plugins/clipboard/clipboardplugin.cpp @@ -1,51 +1,51 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "clipboardplugin.h" #include "clipboardlistener.h" #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_clipboard.json", registerPlugin< ClipboardPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CLIPBOARD, "kdeconnect.plugin.clipboard") ClipboardPlugin::ClipboardPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { connect(ClipboardListener::instance(), &ClipboardListener::clipboardChanged, this, &ClipboardPlugin::propagateClipboard); } void ClipboardPlugin::propagateClipboard(const QString& content) { - NetworkPacket np(PACKET_TYPE_CLIPBOARD, {{"content", content}}); + NetworkPacket np(PACKET_TYPE_CLIPBOARD, {{QStringLiteral("content"), content}}); sendPacket(np); } bool ClipboardPlugin::receivePacket(const NetworkPacket& np) { QString content = np.get(QStringLiteral("content")); ClipboardListener::instance()->setText(content); return true; } #include "clipboardplugin.moc" diff --git a/plugins/contacts/contactsplugin.cpp b/plugins/contacts/contactsplugin.cpp index d0778c94..dfe2788a 100644 --- a/plugins/contacts/contactsplugin.cpp +++ b/plugins/contacts/contactsplugin.cpp @@ -1,214 +1,214 @@ /** * Copyright 2018 Simon Redman * * 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 . */ #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KdeConnectPluginFactory, "kdeconnect_contacts.json", registerPlugin(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CONTACTS, "kdeconnect.plugin.contacts") ContactsPlugin::ContactsPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { - vcardsPath = QString(*vcardsLocation).append("/kdeconnect-").append(device()->id()); + vcardsPath = QString(*vcardsLocation).append(QStringLiteral("/kdeconnect-").append(device()->id())); // Register custom types with dbus qRegisterMetaType("uID"); qDBusRegisterMetaType(); qRegisterMetaType("uIDList_t"); qDBusRegisterMetaType(); // Create the storage directory if it doesn't exist if (!QDir().mkpath(vcardsPath)) { qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "Unable to create VCard directory"; } qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Contacts constructor for device " << device()->name(); } void ContactsPlugin::connected() { synchronizeRemoteWithLocal(); } bool ContactsPlugin::receivePacket(const NetworkPacket& np) { //qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Packet Received for device " << device()->name(); //qCDebug(KDECONNECT_PLUGIN_CONTACTS) << np.body(); if (np.type() == PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS) { return handleResponseUIDsTimestamps(np); } else if (np.type() == PACKET_TYPE_CONTACTS_RESPONSE_VCARDS) { return handleResponseVCards(np); } else { // Is this check necessary? qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Unknown packet type received from device: " << device()->name() << ". Maybe you need to upgrade KDE Connect?"; return false; } } void ContactsPlugin::synchronizeRemoteWithLocal() { sendRequest(PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMP); } bool ContactsPlugin::handleResponseUIDsTimestamps(const NetworkPacket& np) { - if (!np.has("uids")) { + if (!np.has(QStringLiteral("uids"))) { qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseUIDsTimestamps:" << "Malformed packet does not have uids key"; return false; } uIDList_t uIDsToUpdate; QDir vcardsDir(vcardsPath); // Get a list of all file info in this directory // Clean out IDs returned from the remote. Anything leftover should be deleted - QFileInfoList localVCards = vcardsDir.entryInfoList( { "*.vcard", "*.vcf" }); + QFileInfoList localVCards = vcardsDir.entryInfoList({QStringLiteral("*.vcard"), QStringLiteral("*.vcf")}); - const QStringList& uIDs = np.get("uids"); + const QStringList& uIDs = np.get(QStringLiteral("uids")); // Check local storage for the contacts: // If the contact is not found in local storage, request its vcard be sent // If the contact is in local storage but not reported, delete it // If the contact is in local storage, compare its timestamp. If different, request the contact for (const QString& ID : uIDs) { QString filename = vcardsDir.filePath(ID + VCARD_EXTENSION); QFile vcardFile(filename); if (!QFile().exists(filename)) { // We do not have a vcard for this contact. Request it. uIDsToUpdate.push_back(ID); continue; } // Remove this file from the list of known files QFileInfo fileInfo(vcardFile); bool success = localVCards.removeOne(fileInfo); Q_ASSERT(success); // We should have always been able to remove the existing file from our listing // Check if the vcard needs to be updated if (!vcardFile.open(QIODevice::ReadOnly)) { qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseUIDsTimestamps:" << "Unable to open" << filename << "to read even though it was reported to exist"; continue; } QTextStream fileReadStream(&vcardFile); QString line; while (!fileReadStream.atEnd()) { fileReadStream >> line; // TODO: Check that the saved ID is the same as the one we were expecting. This requires parsing the VCard - if (!line.startsWith("X-KDECONNECT-TIMESTAMP:")) { + if (!line.startsWith(QStringLiteral("X-KDECONNECT-TIMESTAMP:"))) { continue; } QStringList parts = line.split(QLatin1Char(':')); QString timestamp = parts[1]; qint32 remoteTimestamp = np.get(ID); qint32 localTimestamp = timestamp.toInt(); if (!(localTimestamp == remoteTimestamp)) { uIDsToUpdate.push_back(ID); } } } // Delete all locally-known files which were not reported by the remote device for (const QFileInfo& unknownFile : localVCards) { QFile toDelete(unknownFile.filePath()); toDelete.remove(); } sendRequestWithIDs(PACKET_TYPE_CONTACTS_REQUEST_VCARDS_BY_UIDS, uIDsToUpdate); return true; } bool ContactsPlugin::handleResponseVCards(const NetworkPacket& np) { - if (!np.has("uids")) { + if (!np.has(QStringLiteral("uids"))) { qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" << "Malformed packet does not have uids key"; return false; } QDir vcardsDir(vcardsPath); - const QStringList& uIDs = np.get("uids"); + const QStringList& uIDs = np.get(QStringLiteral("uids")); // Loop over all IDs, extract the VCard from the packet and write the file for (const auto& ID : uIDs) { //qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Got VCard:" << np.get(ID); QString filename = vcardsDir.filePath(ID + VCARD_EXTENSION); QFile vcardFile(filename); bool vcardFileOpened = vcardFile.open(QIODevice::WriteOnly); // Want to smash anything that might have already been there if (!vcardFileOpened) { qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" << "Unable to open" << filename; continue; } QTextStream fileWriteStream(&vcardFile); const QString& vcard = np.get(ID); fileWriteStream << vcard; } qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" << "Got" << uIDs.size() << "VCards"; Q_EMIT localCacheSynchronized(uIDs); return true; } bool ContactsPlugin::sendRequest(const QString& packetType) { NetworkPacket np(packetType); bool success = sendPacket(np); qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "sendRequest: Sending " << packetType << success; return success; } bool ContactsPlugin::sendRequestWithIDs(const QString& packetType, const uIDList_t& uIDs) { NetworkPacket np(packetType); - np.set("uids", uIDs); + np.set(QStringLiteral("uids"), uIDs); bool success = sendPacket(np); return success; } QString ContactsPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/contacts"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/contacts"); } #include "contactsplugin.moc" diff --git a/plugins/contacts/contactsplugin.h b/plugins/contacts/contactsplugin.h index 56a6c978..580079fc 100644 --- a/plugins/contacts/contactsplugin.h +++ b/plugins/contacts/contactsplugin.h @@ -1,162 +1,162 @@ /** * Copyright 2018 Simon Redman * * 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 . */ #ifndef CONTACTSPLUGIN_H #define CONTACTSPLUGIN_H class QObject; #include #include /** * Used to request the device send the unique ID and last-changed timestamp of every contact */ #define PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMP QStringLiteral("kdeconnect.contacts.request_all_uids_timestamps") /** * Used to request the vcards for the contacts corresponding to a list of UIDs * * It shall contain the key "uids", which will have a list of uIDs (long int, as string) */ #define PACKET_TYPE_CONTACTS_REQUEST_VCARDS_BY_UIDS QStringLiteral("kdeconnect.contacts.request_vcards_by_uid") /** * Response indicating the package contains a list of all contact uIDs and last-changed timestamps * * It shall contain the key "uids", which will mark a list of uIDs (long int, as string) * then, for each UID, there shall be a field with the key of that UID and the value of the timestamp (int, as string) * * For example: * ( 'uids' : ['1', '3', '15'], * '1' : '973486597', * '3' : '973485443', * '15' : '973492390' ) * * The returned IDs can be used in future requests for more information about the contact */ #define PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS QStringLiteral("kdeconnect.contacts.response_uids_timestamps") /** * Response indicating the package contains a list of contact vcards * * It shall contain the key "uids", which will mark a list of uIDs (long int, as string) * then, for each UID, there shall be a field with the key of that UID and the value of the remote's vcard for that contact * * For example: * ( 'uids' : ['1', '3', '15'], * '1' : 'BEGIN:VCARD\n....\nEND:VCARD', * '3' : 'BEGIN:VCARD\n....\nEND:VCARD', * '15' : 'BEGIN:VCARD\n....\nEND:VCARD' ) */ #define PACKET_TYPE_CONTACTS_RESPONSE_VCARDS QStringLiteral("kdeconnect.contacts.response_vcards") /** * Where the synchronizer will write vcards and other metadata * TODO: Per-device folders since each device *will* have different uIDs */ Q_GLOBAL_STATIC_WITH_ARGS( QString, vcardsLocation, - (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + ("/kpeoplevcard"))) + (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString::fromLatin1("/kpeoplevcard"))) #define VCARD_EXTENSION QStringLiteral(".vcf") #define METADATA_EXTENSION QStringLiteral(".meta") typedef QString uID; Q_DECLARE_METATYPE(uID) typedef QStringList uIDList_t; Q_DECLARE_METATYPE(uIDList_t) class Q_DECL_EXPORT ContactsPlugin : public KdeConnectPlugin { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.contacts") public: explicit ContactsPlugin(QObject* parent, const QVariantList& args); bool receivePacket(const NetworkPacket& np) override; void connected() override; QString dbusPath () const override; protected: /** * Path where this instance of the plugin stores its synchronized contacts */ QString vcardsPath; public Q_SLOTS: /** * Query the remote device for all its uIDs and last-changed timestamps, then: * Delete any contacts which are known locally but not reported by the remote * Update any contacts which are known locally but have an older timestamp * Add any contacts which are not known locally but are reported by the remote */ Q_SCRIPTABLE void synchronizeRemoteWithLocal(); public: Q_SIGNALS: /** * Emitted to indicate that we have locally cached all remote contacts * * @param newContacts The list of just-synchronized contacts */ Q_SCRIPTABLE void localCacheSynchronized(const uIDList_t& newContacts); protected: /** * Handle a packet of type PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS * * For every uID in the reply: * Delete any from local storage if it does not appear in the reply * Compare the modified timestamp for each in the reply and update any which should have changed * Request the details any IDs which were not locally cached */ bool handleResponseUIDsTimestamps(const NetworkPacket&); /** * Handle a packet of type PACKET_TYPE_CONTACTS_RESPONSE_VCARDS */ bool handleResponseVCards(const NetworkPacket&); /** * Send a request-type packet which contains no body * * @return True if the send was successful, false otherwise */ bool sendRequest(const QString& packetType); /** * Send a request-type packet which has a body with the key 'uids' and the value the list of * specified uIDs * * @param packageType Type of package to send * @param uIDs List of uIDs to request * @return True if the send was successful, false otherwise */ bool sendRequestWithIDs(const QString& packetType, const uIDList_t& uIDs); }; #endif // CONTACTSPLUGIN_H diff --git a/plugins/findmyphone/findmyphoneplugin.cpp b/plugins/findmyphone/findmyphoneplugin.cpp index 835231ad..3fb8f252 100644 --- a/plugins/findmyphone/findmyphoneplugin.cpp +++ b/plugins/findmyphone/findmyphoneplugin.cpp @@ -1,57 +1,57 @@ /** * Copyright 2014 Apoorv Parle * Copyright 2015 David Edmundson * * 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 . */ #include "findmyphoneplugin.h" #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_findmyphone.json", registerPlugin< FindMyPhonePlugin >(); ) FindMyPhonePlugin::FindMyPhonePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } FindMyPhonePlugin::~FindMyPhonePlugin() { } bool FindMyPhonePlugin::receivePacket(const NetworkPacket& np) { Q_UNUSED(np); return false; } void FindMyPhonePlugin::ring() { NetworkPacket np(PACKET_TYPE_FINDMYPHONE_REQUEST); sendPacket(np); } QString FindMyPhonePlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/findmyphone"; + return QString::fromLatin1("/modules/kdeconnect/devices/") + device()->id() + QString::fromLatin1("/findmyphone"); } #include "findmyphoneplugin.moc" diff --git a/plugins/findthisdevice/findthisdeviceplugin.cpp b/plugins/findthisdevice/findthisdeviceplugin.cpp index e1018518..824c1d6d 100644 --- a/plugins/findthisdevice/findthisdeviceplugin.cpp +++ b/plugins/findthisdevice/findthisdeviceplugin.cpp @@ -1,130 +1,130 @@ /** * Copyright 2018 Friedrich W. H. Kossebau * * 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 . */ #include "findthisdeviceplugin.h" // KF #include #ifndef Q_OS_WIN #include #include #endif // Qt #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KdeConnectPluginFactory, "kdeconnect_findthisdevice.json", registerPlugin();) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_FINDTHISDEVICE, "kdeconnect.plugin.findthisdevice") FindThisDevicePlugin::FindThisDevicePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } FindThisDevicePlugin::~FindThisDevicePlugin() = default; void FindThisDevicePlugin::connected() { } bool FindThisDevicePlugin::receivePacket(const NetworkPacket& np) { Q_UNUSED(np); const QString soundFilename = config()->get(QStringLiteral("ringtone"), defaultSound()); QUrl soundURL; #ifdef Q_OS_WIN QString winDirPath = qEnvironmentVariable("WINDIR") + QStringLiteral("/media"); if (!winDirPath.isEmpty()) { soundURL = QUrl::fromUserInput(soundFilename, winDirPath, QUrl::AssumeLocalFile); } else { qCWarning(KDECONNECT_PLUGIN_FINDTHISDEVICE) << "Not playing sounds, system doesn't know WINDIR : " << soundFilename; } #else const auto dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &dataLocation : dataLocations) { soundURL = QUrl::fromUserInput(soundFilename, dataLocation + QStringLiteral("/sounds"), QUrl::AssumeLocalFile); if (soundURL.isLocalFile()) { if (QFile::exists(soundURL.toLocalFile())) { break; } } else { if (soundURL.isValid()) { break; } } soundURL.clear(); } #endif if (soundURL.isEmpty()) { qCWarning(KDECONNECT_PLUGIN_FINDTHISDEVICE) << "Not playing sounds, could not find ring tone" << soundFilename; return true; } QMediaPlayer* player = new QMediaPlayer; player->setAudioRole(QAudio::Role(QAudio::NotificationRole)); player->setMedia(soundURL); player->setVolume(100); player->play(); #ifndef Q_OS_WIN const auto sinks = PulseAudioQt::Context::instance()->sinks(); QVector mutedSinks; for (auto sink : sinks) { if (sink->isMuted()) { sink->setMuted(false); mutedSinks.append(sink); } } connect(player, &QMediaPlayer::stateChanged, this, [player, mutedSinks]{ player->deleteLater(); for (auto sink : qAsConst(mutedSinks)) { sink->setMuted(true); } }); #endif // TODO: ensure to use built-in loudspeakers return true; } QString FindThisDevicePlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/findthisdevice"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/findthisdevice"); } #include "findthisdeviceplugin.moc" diff --git a/plugins/lockdevice/lockdeviceplugin.cpp b/plugins/lockdevice/lockdeviceplugin.cpp index efafb151..b5327341 100644 --- a/plugins/lockdevice/lockdeviceplugin.cpp +++ b/plugins/lockdevice/lockdeviceplugin.cpp @@ -1,103 +1,103 @@ /** * Copyright 2015 Aleix Pol Gonzalez * * 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 . */ #include "lockdeviceplugin.h" #include #include #include #include #include "screensaverdbusinterface.h" #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectLockPluginFactory, "kdeconnect_lockdevice.json", registerPlugin(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_LOCKREMOTE, "kdeconnect.plugin.lock") LockDevicePlugin::LockDevicePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_remoteLocked(false) , m_iface(nullptr) { } LockDevicePlugin::~LockDevicePlugin() { delete m_iface; } bool LockDevicePlugin::isLocked() const { return m_remoteLocked; } void LockDevicePlugin::setLocked(bool locked) { - NetworkPacket np(PACKET_TYPE_LOCK_REQUEST, {{"setLocked", locked}}); + NetworkPacket np(PACKET_TYPE_LOCK_REQUEST, {{QStringLiteral("setLocked"), locked}}); sendPacket(np); } bool LockDevicePlugin::receivePacket(const NetworkPacket & np) { if (np.has(QStringLiteral("isLocked"))) { bool locked = np.get(QStringLiteral("isLocked")); if (m_remoteLocked != locked) { m_remoteLocked = locked; Q_EMIT lockedChanged(locked); } } bool sendState = np.has(QStringLiteral("requestLocked")); if (np.has(QStringLiteral("setLocked"))) { iface()->SetActive(np.get(QStringLiteral("setLocked"))); sendState = true; } if (sendState) { - NetworkPacket np(PACKET_TYPE_LOCK, QVariantMap {{"isLocked", QVariant::fromValue(iface()->GetActive())}}); + NetworkPacket np(PACKET_TYPE_LOCK, QVariantMap {{QStringLiteral("isLocked"), QVariant::fromValue(iface()->GetActive())}}); sendPacket(np); } return true; } OrgFreedesktopScreenSaverInterface* LockDevicePlugin::iface() { if (!m_iface) { m_iface = new OrgFreedesktopScreenSaverInterface(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/org/freedesktop/ScreenSaver"), DbusHelper::sessionBus()); if(!m_iface->isValid()) qCWarning(KDECONNECT_PLUGIN_LOCKREMOTE) << "Couldn't connect to the ScreenSaver interface"; } return m_iface; } void LockDevicePlugin::connected() { - NetworkPacket np(PACKET_TYPE_LOCK_REQUEST, {{"requestLocked", QVariant()}}); + NetworkPacket np(PACKET_TYPE_LOCK_REQUEST, {{QStringLiteral("requestLocked"), QVariant()}}); sendPacket(np); } QString LockDevicePlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/lockdevice"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/lockdevice"); } #include "lockdeviceplugin.moc" diff --git a/plugins/mpriscontrol/mpriscontrolplugin-win.h b/plugins/mpriscontrol/mpriscontrolplugin-win.h index db47f1bd..600f531c 100644 --- a/plugins/mpriscontrol/mpriscontrolplugin-win.h +++ b/plugins/mpriscontrol/mpriscontrolplugin-win.h @@ -1,29 +1,29 @@ #ifndef MPRISCONTROLPLUGINWIN_H #define MPRISCONTROLPLUGINWIN_H #include #include #include #define PLAYERNAME QStringLiteral("Media Player") #define PACKET_TYPE_MPRIS QStringLiteral("kdeconnect.mpris") Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_MPRIS) class MprisControlPlugin : public KdeConnectPlugin { Q_OBJECT public: explicit MprisControlPlugin(QObject *parent, const QVariantList &args); bool receivePacket(const NetworkPacket &np) override; void connected() override {} private: - const QString playername = "Media Player"; + const QString playername = PLAYERNAME; }; #endif //MPRISCONTROLPLUGINWIN_H \ No newline at end of file diff --git a/plugins/mpriscontrol/mpriscontrolplugin.cpp b/plugins/mpriscontrol/mpriscontrolplugin.cpp index c8603f90..6d59eb6a 100644 --- a/plugins/mpriscontrol/mpriscontrolplugin.cpp +++ b/plugins/mpriscontrol/mpriscontrolplugin.cpp @@ -1,382 +1,382 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "mpriscontrolplugin.h" #include #include #include #include #include #include #include #include #include #include "mprisdbusinterface.h" #include "propertiesdbusinterface.h" K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_mpriscontrol.json", registerPlugin< MprisControlPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_MPRIS, "kdeconnect.plugin.mpris") MprisPlayer::MprisPlayer(const QString& serviceName, const QString& dbusObjectPath, const QDBusConnection& busConnection) : m_serviceName(serviceName) , m_propertiesInterface(new OrgFreedesktopDBusPropertiesInterface(serviceName, dbusObjectPath, busConnection)) , m_mediaPlayer2PlayerInterface(new OrgMprisMediaPlayer2PlayerInterface(serviceName, dbusObjectPath, busConnection)) { m_mediaPlayer2PlayerInterface->setTimeout(500); } MprisControlPlugin::MprisControlPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , prevVolume(-1) { m_watcher = new QDBusServiceWatcher(QString(), DbusHelper::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); // TODO: QDBusConnectionInterface::serviceOwnerChanged is deprecated, maybe query org.freedesktop.DBus directly? connect(DbusHelper::sessionBus().interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, &MprisControlPlugin::serviceOwnerChanged); //Add existing interfaces const QStringList services = DbusHelper::sessionBus().interface()->registeredServiceNames().value(); for (const QString& service : services) { // The string doesn't matter, it just needs to be empty/non-empty serviceOwnerChanged(service, QLatin1String(""), QStringLiteral("1")); } } // Copied from the mpris2 dataengine in the plasma-workspace repository void MprisControlPlugin::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner, const QString& newOwner) { if (!serviceName.startsWith(QLatin1String("org.mpris.MediaPlayer2."))) return; if (!oldOwner.isEmpty()) { qCDebug(KDECONNECT_PLUGIN_MPRIS) << "MPRIS service" << serviceName << "just went offline"; removePlayer(serviceName); } if (!newOwner.isEmpty()) { qCDebug(KDECONNECT_PLUGIN_MPRIS) << "MPRIS service" << serviceName << "just came online"; addPlayer(serviceName); } } void MprisControlPlugin::addPlayer(const QString& service) { const QString mediaPlayerObjectPath = QStringLiteral("/org/mpris/MediaPlayer2"); // estimate identifier string QDBusInterface mprisInterface(service, mediaPlayerObjectPath, QStringLiteral("org.mpris.MediaPlayer2")); //FIXME: This call hangs and returns an empty string if KDED is still starting! QString identity = mprisInterface.property("Identity").toString(); if (identity.isEmpty()) { identity = service.mid(sizeof("org.mpris.MediaPlayer2")); } QString uniqueName = identity; for (int i = 2; playerList.contains(uniqueName); ++i) { uniqueName = identity + QLatin1String(" [") + QString::number(i) + QLatin1Char(']'); } MprisPlayer player(service, mediaPlayerObjectPath, DbusHelper::sessionBus()); playerList.insert(uniqueName, player); connect(player.propertiesInterface(), &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, this, &MprisControlPlugin::propertiesChanged); connect(player.mediaPlayer2PlayerInterface(), &OrgMprisMediaPlayer2PlayerInterface::Seeked, this, &MprisControlPlugin::seeked); qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Mpris addPlayer" << service << "->" << uniqueName; sendPlayerList(); } void MprisControlPlugin::seeked(qlonglong position){ //qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Seeked in player"; OrgMprisMediaPlayer2PlayerInterface* mediaPlayer2PlayerInterface = (OrgMprisMediaPlayer2PlayerInterface*)sender(); const auto end = playerList.constEnd(); const auto it = std::find_if(playerList.constBegin(), end, [mediaPlayer2PlayerInterface](const MprisPlayer& player) { return (player.mediaPlayer2PlayerInterface() == mediaPlayer2PlayerInterface); }); if (it == end) { qCWarning(KDECONNECT_PLUGIN_MPRIS) << "Seeked signal received for no longer tracked service" << mediaPlayer2PlayerInterface->service(); return; } const QString& playerName = it.key(); NetworkPacket np(PACKET_TYPE_MPRIS, { - {"pos", position/1000}, //Send milis instead of nanos - {"player", playerName} + {QStringLiteral("pos"), position/1000}, //Send milis instead of nanos + {QStringLiteral("player"), playerName} }); sendPacket(np); } void MprisControlPlugin::propertiesChanged(const QString& propertyInterface, const QVariantMap& properties) { Q_UNUSED(propertyInterface); OrgFreedesktopDBusPropertiesInterface* propertiesInterface = (OrgFreedesktopDBusPropertiesInterface*)sender(); const auto end = playerList.constEnd(); const auto it = std::find_if(playerList.constBegin(), end, [propertiesInterface](const MprisPlayer& player) { return (player.propertiesInterface() == propertiesInterface); }); if (it == end) { qCWarning(KDECONNECT_PLUGIN_MPRIS) << "PropertiesChanged signal received for no longer tracked service" << propertiesInterface->service(); return; } OrgMprisMediaPlayer2PlayerInterface* const mediaPlayer2PlayerInterface = it.value().mediaPlayer2PlayerInterface(); const QString& playerName = it.key(); NetworkPacket np(PACKET_TYPE_MPRIS); bool somethingToSend = false; if (properties.contains(QStringLiteral("Volume"))) { int volume = (int) (properties[QStringLiteral("Volume")].toDouble()*100); if (volume != prevVolume) { np.set(QStringLiteral("volume"),volume); prevVolume = volume; somethingToSend = true; } } if (properties.contains(QStringLiteral("Metadata"))) { QDBusArgument bullshit = qvariant_cast(properties[QStringLiteral("Metadata")]); QVariantMap nowPlayingMap; bullshit >> nowPlayingMap; mprisPlayerMetadataToNetworkPacket(np, nowPlayingMap); somethingToSend = true; } if (properties.contains(QStringLiteral("PlaybackStatus"))) { bool playing = (properties[QStringLiteral("PlaybackStatus")].toString() == QLatin1String("Playing")); np.set(QStringLiteral("isPlaying"), playing); somethingToSend = true; } if (properties.contains(QStringLiteral("CanPause"))) { np.set(QStringLiteral("canPause"), properties[QStringLiteral("CanPause")].toBool()); somethingToSend = true; } if (properties.contains(QStringLiteral("CanPlay"))) { np.set(QStringLiteral("canPlay"), properties[QStringLiteral("CanPlay")].toBool()); somethingToSend = true; } if (properties.contains(QStringLiteral("CanGoNext"))) { np.set(QStringLiteral("canGoNext"), properties[QStringLiteral("CanGoNext")].toBool()); somethingToSend = true; } if (properties.contains(QStringLiteral("CanGoPrevious"))) { np.set(QStringLiteral("canGoPrevious"), properties[QStringLiteral("CanGoPrevious")].toBool()); somethingToSend = true; } if (properties.contains(QStringLiteral("CanSeek"))) { np.set(QStringLiteral("canSeek"), properties[QStringLiteral("CanSeek")].toBool()); somethingToSend = true; } if (somethingToSend) { np.set(QStringLiteral("player"), playerName); // Always also update the position if (mediaPlayer2PlayerInterface->canSeek()) { long long pos = mediaPlayer2PlayerInterface->position(); np.set(QStringLiteral("pos"), pos/1000); //Send milis instead of nanos } sendPacket(np); } } void MprisControlPlugin::removePlayer(const QString& serviceName) { const auto end = playerList.end(); const auto it = std::find_if(playerList.begin(), end, [serviceName](const MprisPlayer& player) { return (player.serviceName() == serviceName); }); if (it == end) { qCWarning(KDECONNECT_PLUGIN_MPRIS) << "Could not find player for serviceName" << serviceName; return; } const QString& playerName = it.key(); qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Mpris removePlayer" << serviceName << "->" << playerName; playerList.erase(it); sendPlayerList(); } bool MprisControlPlugin::sendAlbumArt(const NetworkPacket& np) { const QString player = np.get(QStringLiteral("player")); auto it = playerList.find(player); bool valid_player = (it != playerList.end()); if (!valid_player) { return false; } //Get mpris information auto& mprisInterface = *it.value().mediaPlayer2PlayerInterface(); QVariantMap nowPlayingMap = mprisInterface.metadata(); //Check if the supplied album art url indeed belongs to this mpris player QUrl playerAlbumArtUrl{nowPlayingMap[QStringLiteral("mpris:artUrl")].toString()}; QString requestedAlbumArtUrl = np.get(QStringLiteral("albumArtUrl")); if (!playerAlbumArtUrl.isValid() || playerAlbumArtUrl != QUrl(requestedAlbumArtUrl)) { return false; } //Only support sending local files - if (playerAlbumArtUrl.scheme() != "file") { + if (playerAlbumArtUrl.scheme() != QStringLiteral("file")) { return false; } //Open the file to send QSharedPointer art{new QFile(playerAlbumArtUrl.toLocalFile())}; //Send the album art as payload NetworkPacket answer(PACKET_TYPE_MPRIS); answer.set(QStringLiteral("transferringAlbumArt"), true); answer.set(QStringLiteral("player"), player); answer.set(QStringLiteral("albumArtUrl"), requestedAlbumArtUrl); answer.setPayload(art, art->size()); sendPacket(answer); return true; } bool MprisControlPlugin::receivePacket (const NetworkPacket& np) { if (np.has(QStringLiteral("playerList"))) { return false; //Whoever sent this is an mpris client and not an mpris control! } if (np.has(QStringLiteral("albumArtUrl"))) { return sendAlbumArt(np); } //Send the player list const QString player = np.get(QStringLiteral("player")); auto it = playerList.find(player); bool valid_player = (it != playerList.end()); if (!valid_player || np.get(QStringLiteral("requestPlayerList"))) { sendPlayerList(); if (!valid_player) { return true; } } //Do something to the mpris interface const QString& serviceName = it.value().serviceName(); // turn from pointer to reference to keep the patch diff small, // actual patch would change all "mprisInterface." into "mprisInterface->" auto& mprisInterface = *it.value().mediaPlayer2PlayerInterface(); if (np.has(QStringLiteral("action"))) { const QString& action = np.get(QStringLiteral("action")); //qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Calling action" << action << "in" << serviceName; //TODO: Check for valid actions, currently we trust anything the other end sends us mprisInterface.call(action); } if (np.has(QStringLiteral("setVolume"))) { double volume = np.get(QStringLiteral("setVolume"))/100.f; qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Setting volume" << volume << "to" << serviceName; mprisInterface.setVolume(volume); } if (np.has(QStringLiteral("Seek"))) { int offset = np.get(QStringLiteral("Seek")); //qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Seeking" << offset << "to" << serviceName; mprisInterface.Seek(offset); } if (np.has(QStringLiteral("SetPosition"))){ qlonglong position = np.get(QStringLiteral("SetPosition"),0)*1000; qlonglong seek = position - mprisInterface.position(); //qCDebug(KDECONNECT_PLUGIN_MPRIS) << "Setting position by seeking" << seek << "to" << serviceName; mprisInterface.Seek(seek); } //Send something read from the mpris interface NetworkPacket answer(PACKET_TYPE_MPRIS); bool somethingToSend = false; if (np.get(QStringLiteral("requestNowPlaying"))) { QVariantMap nowPlayingMap = mprisInterface.metadata(); mprisPlayerMetadataToNetworkPacket(answer, nowPlayingMap); qlonglong pos = mprisInterface.position(); answer.set(QStringLiteral("pos"), pos/1000); bool playing = (mprisInterface.playbackStatus() == QLatin1String("Playing")); answer.set(QStringLiteral("isPlaying"), playing); answer.set(QStringLiteral("canPause"), mprisInterface.canPause()); answer.set(QStringLiteral("canPlay"), mprisInterface.canPlay()); answer.set(QStringLiteral("canGoNext"), mprisInterface.canGoNext()); answer.set(QStringLiteral("canGoPrevious"), mprisInterface.canGoPrevious()); answer.set(QStringLiteral("canSeek"), mprisInterface.canSeek()); somethingToSend = true; } if (np.get(QStringLiteral("requestVolume"))) { int volume = (int)(mprisInterface.volume() * 100); answer.set(QStringLiteral("volume"),volume); somethingToSend = true; } if (somethingToSend) { answer.set(QStringLiteral("player"), player); sendPacket(answer); } return true; } void MprisControlPlugin::sendPlayerList() { NetworkPacket np(PACKET_TYPE_MPRIS); np.set(QStringLiteral("playerList"),playerList.keys()); np.set(QStringLiteral("supportAlbumArtPayload"), true); sendPacket(np); } void MprisControlPlugin::mprisPlayerMetadataToNetworkPacket(NetworkPacket& np, const QVariantMap& nowPlayingMap) const { QString title = nowPlayingMap[QStringLiteral("xesam:title")].toString(); QString artist = nowPlayingMap[QStringLiteral("xesam:artist")].toString(); QString album = nowPlayingMap[QStringLiteral("xesam:album")].toString(); QString albumArtUrl = nowPlayingMap[QStringLiteral("mpris:artUrl")].toString(); QString nowPlaying = title; if (!artist.isEmpty()) { - nowPlaying = artist + " - " + title; + nowPlaying = artist + QStringLiteral(" - ") + title; } np.set(QStringLiteral("title"), title); np.set(QStringLiteral("artist"), artist); np.set(QStringLiteral("album"), album); np.set(QStringLiteral("albumArtUrl"), albumArtUrl); np.set(QStringLiteral("nowPlaying"), nowPlaying); bool hasLength = false; long long length = nowPlayingMap[QStringLiteral("mpris:length")].toLongLong(&hasLength) / 1000; //nanoseconds to milliseconds if (!hasLength) { length = -1; } np.set(QStringLiteral("length"), length); } #include "mpriscontrolplugin.moc" diff --git a/plugins/mprisremote/mprisremoteplugin.cpp b/plugins/mprisremote/mprisremoteplugin.cpp index 2e9f2409..be89115e 100644 --- a/plugins/mprisremote/mprisremoteplugin.cpp +++ b/plugins/mprisremote/mprisremoteplugin.cpp @@ -1,206 +1,206 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "mprisremoteplugin.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_mprisremote.json", registerPlugin< MprisRemotePlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_MPRISREMOTE, "kdeconnect.plugin.mprisremote") MprisRemotePlugin::MprisRemotePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_currentPlayer() , m_players() { } MprisRemotePlugin::~MprisRemotePlugin() { } bool MprisRemotePlugin::receivePacket(const NetworkPacket& np) { if (np.type() != PACKET_TYPE_MPRIS) return false; if (np.has(QStringLiteral("player"))) { const QString player = np.get(QStringLiteral("player")); if(!m_players.contains(player)) { m_players[player] = new MprisRemotePlayer(); } m_players[player]->parseNetworkPacket(np); } if (np.has(QStringLiteral("playerList"))) { QStringList players = np.get(QStringLiteral("playerList")); qDeleteAll(m_players); m_players.clear(); for (const QString& player : players) { m_players[player] = new MprisRemotePlayer(); requestPlayerStatus(player); } if (m_players.empty()) { m_currentPlayer = QString(); } else if (!m_players.contains(m_currentPlayer)) { m_currentPlayer = m_players.firstKey(); } } Q_EMIT propertiesChanged(); return true; } long MprisRemotePlugin::position() const { auto player = m_players.value(m_currentPlayer); return player ? player->position() : 0; } QString MprisRemotePlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/mprisremote"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/mprisremote"); } void MprisRemotePlugin::requestPlayerStatus(const QString& player) { NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, { - {"player", player}, - {"requestNowPlaying", true}, - {"requestVolume", true}} + {QStringLiteral("player"), player}, + {QStringLiteral("requestNowPlaying"), true}, + {QStringLiteral("requestVolume"), true}} ); sendPacket(np); } void MprisRemotePlugin::requestPlayerList() { - NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, {{"requestPlayerList", true}}); + NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, {{QStringLiteral("requestPlayerList"), true}}); sendPacket(np); } void MprisRemotePlugin::sendAction(const QString& action) { NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, { - {"player", m_currentPlayer}, - {"action", action} + {QStringLiteral("player"), m_currentPlayer}, + {QStringLiteral("action"), action} }); sendPacket(np); } void MprisRemotePlugin::seek(int offset) const { NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, { - {"player", m_currentPlayer}, - {"Seek", offset}}); + {QStringLiteral("player"), m_currentPlayer}, + {QStringLiteral("Seek"), offset}}); sendPacket(np); } void MprisRemotePlugin::setVolume(int volume) { NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, { - {"player", m_currentPlayer}, - {"setVolume",volume} + {QStringLiteral("player"), m_currentPlayer}, + {QStringLiteral("setVolume"), volume} }); sendPacket(np); } void MprisRemotePlugin::setPosition(int position) { NetworkPacket np(PACKET_TYPE_MPRIS_REQUEST, { - {"player", m_currentPlayer}, - {"SetPosition", position} + {QStringLiteral("player"), m_currentPlayer}, + {QStringLiteral("SetPosition"), position} }); sendPacket(np); m_players[m_currentPlayer]->setPosition(position); } void MprisRemotePlugin::setPlayer(const QString& player) { if (m_currentPlayer != player) { m_currentPlayer = player; requestPlayerStatus(player); Q_EMIT propertiesChanged(); } } bool MprisRemotePlugin::isPlaying() const { auto player = m_players.value(m_currentPlayer); return player ? player->playing() : false; } int MprisRemotePlugin::length() const { auto player = m_players.value(m_currentPlayer); return player ? player->length() : 0; } int MprisRemotePlugin::volume() const { auto player = m_players.value(m_currentPlayer); return player ? player->volume() : 0; } QString MprisRemotePlugin::player() const { if (m_currentPlayer.isEmpty()) return QString(); return m_currentPlayer; } QStringList MprisRemotePlugin::playerList() const { return m_players.keys(); } QString MprisRemotePlugin::nowPlaying() const { auto player = m_players.value(m_currentPlayer); return player ? player->nowPlaying() : QString(); } QString MprisRemotePlugin::title() const { auto player = m_players.value(m_currentPlayer); return player ? player->title() : QString(); } QString MprisRemotePlugin::album() const { auto player = m_players.value(m_currentPlayer); return player ? player->album() : QString(); } QString MprisRemotePlugin::artist() const { auto player = m_players.value(m_currentPlayer); return player ? player->artist() : QString(); } #include "mprisremoteplugin.moc" diff --git a/plugins/notifications/notification.cpp b/plugins/notifications/notification.cpp index 52dfa828..845f04c2 100644 --- a/plugins/notifications/notification.cpp +++ b/plugins/notifications/notification.cpp @@ -1,217 +1,217 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "notification.h" #include "notification_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include QMap Notification::s_downloadsInProgress; Notification::Notification(const NetworkPacket& np, const Device* device, QObject* parent) : QObject(parent) , m_device(device) { //Make a own directory for each user so noone can see each others icons QString username; #ifdef Q_OS_WIN - username = qgetenv("USERNAME"); + username = QString::fromLatin1(qgetenv("USERNAME")); #else - username = qgetenv("USER"); + username = QString::fromLatin1(qgetenv("USER")); #endif m_imagesDir = QDir::temp().absoluteFilePath(QStringLiteral("kdeconnect_") + username); m_imagesDir.mkpath(m_imagesDir.absolutePath()); QFile(m_imagesDir.absolutePath()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); m_ready = false; parseNetworkPacket(np); createKNotification(np); connect(m_notification, QOverload::of(&KNotification::activated), this, [this] (unsigned int actionIndex) { // Do nothing for our own reply action if(!m_requestReplyId.isEmpty() && actionIndex == 1) { return; } // Notification action idices start at 1 Q_EMIT actionTriggered(m_internalId, m_actions[actionIndex - 1]); }); } Notification::~Notification() { } void Notification::dismiss() { if (m_dismissable) { Q_EMIT dismissRequested(m_internalId); } } void Notification::show() { m_ready = true; Q_EMIT ready(); if (!m_silent) { m_notification->sendEvent(); } } void Notification::update(const NetworkPacket& np) { parseNetworkPacket(np); createKNotification(np); } void Notification::createKNotification(const NetworkPacket& np) { if (!m_notification) { m_notification = new KNotification(QStringLiteral("notification"), KNotification::CloseOnTimeout, this); m_notification->setComponentName(QStringLiteral("kdeconnect")); } QString escapedTitle = m_title.toHtmlEscaped(); QString escapedText = m_text.toHtmlEscaped(); QString escapedTicker = m_ticker.toHtmlEscaped(); #if KNOTIFICATIONS_VERSION >= QT_VERSION_CHECK(5, 57, 0) if (NotificationServerInfo::instance().supportedHints().testFlag(NotificationServerInfo::X_KDE_DISPLAY_APPNAME)) { m_notification->setTitle(escapedTitle); m_notification->setText(escapedText); m_notification->setHint(QStringLiteral("x-kde-display-appname"), m_appName.toHtmlEscaped()); } else { #endif m_notification->setTitle(m_appName.toHtmlEscaped()); if (m_title.isEmpty() && m_text.isEmpty()) { m_notification->setText(escapedTicker); } else if (m_appName == m_title) { m_notification->setText(escapedText); } else if (m_title.isEmpty()) { m_notification->setText(escapedText); } else if (m_text.isEmpty()) { m_notification->setText(escapedTitle); } else { - m_notification->setText(escapedTitle + ": " + escapedText); + m_notification->setText(escapedTitle + QStringLiteral(": ") + escapedText); } #if KNOTIFICATIONS_VERSION >= QT_VERSION_CHECK(5, 57, 0) } m_notification->setHint(QStringLiteral("x-kde-origin-name"), m_device->name()); #endif m_hasIcon = m_hasIcon && !m_payloadHash.isEmpty(); if (!m_hasIcon) { applyNoIcon(); show(); } else { m_iconPath = m_imagesDir.absoluteFilePath(m_payloadHash); loadIcon(np); } if (!m_requestReplyId.isEmpty()) { m_actions.prepend(i18n("Reply")); connect(m_notification, &KNotification::action1Activated, this, &Notification::reply, Qt::UniqueConnection); } m_notification->setActions(m_actions); } void Notification::loadIcon(const NetworkPacket& np) { m_ready = false; if (QFileInfo::exists(m_iconPath)) { applyIcon(); show(); } else { FileTransferJob* fileTransferJob = s_downloadsInProgress.value(m_iconPath); if (!fileTransferJob) { fileTransferJob = np.createPayloadTransferJob(QUrl::fromLocalFile(m_iconPath)); fileTransferJob->start(); s_downloadsInProgress[m_iconPath] = fileTransferJob; } connect(fileTransferJob, &FileTransferJob::result, this, [this, fileTransferJob]{ s_downloadsInProgress.remove(m_iconPath); if (fileTransferJob->error()) { qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Error in FileTransferJob: " << fileTransferJob->errorString(); applyNoIcon(); } else { applyIcon(); } show(); }); } } void Notification::applyIcon() { QPixmap icon(m_iconPath, "PNG"); m_notification->setPixmap(icon); } void Notification::applyNoIcon() { //HACK The only way to display no icon at all is trying to load a non-existent icon m_notification->setIconName(QStringLiteral("not_a_real_icon")); } void Notification::reply() { Q_EMIT replyRequested(); } void Notification::parseNetworkPacket(const NetworkPacket& np) { m_internalId = np.get(QStringLiteral("id")); m_appName = np.get(QStringLiteral("appName")); m_ticker = np.get(QStringLiteral("ticker")); m_title = np.get(QStringLiteral("title")); m_text = np.get(QStringLiteral("text")); m_dismissable = np.get(QStringLiteral("isClearable")); m_hasIcon = np.hasPayload(); m_silent = np.get(QStringLiteral("silent")); m_payloadHash = np.get(QStringLiteral("payloadHash")); m_requestReplyId = np.get(QStringLiteral("requestReplyId"), QString()); m_actions.clear(); const auto actions = np.get(QStringLiteral("actions")); for (const QJsonValue& value : actions) { m_actions.append(value.toString()); } } diff --git a/plugins/notifications/notificationsdbusinterface.cpp b/plugins/notifications/notificationsdbusinterface.cpp index f28d749f..210a5476 100644 --- a/plugins/notifications/notificationsdbusinterface.cpp +++ b/plugins/notifications/notificationsdbusinterface.cpp @@ -1,192 +1,192 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "notificationsdbusinterface.h" #include "notification_debug.h" #include "notification.h" #include #include #include #include "notificationsplugin.h" #include "sendreplydialog.h" //In older Qt released, qAsConst isnt available #include "qtcompat_p.h" NotificationsDbusInterface::NotificationsDbusInterface(KdeConnectPlugin* plugin) : QDBusAbstractAdaptor(const_cast(plugin->device())) , m_device(plugin->device()) , m_plugin(plugin) , m_lastId(0) { } NotificationsDbusInterface::~NotificationsDbusInterface() { qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Destroying NotificationsDbusInterface"; } void NotificationsDbusInterface::clearNotifications() { qDeleteAll(m_notifications); m_notifications.clear(); Q_EMIT allNotificationsRemoved(); } QStringList NotificationsDbusInterface::activeNotifications() { return m_notifications.keys(); } void NotificationsDbusInterface::notificationReady() { Notification* noti = static_cast(sender()); disconnect(noti, &Notification::ready, this, &NotificationsDbusInterface::notificationReady); addNotification(noti); } void NotificationsDbusInterface::processPacket(const NetworkPacket& np) { if (np.get(QStringLiteral("isCancel"))) { QString id = np.get(QStringLiteral("id")); // cut off kdeconnect-android's prefix if there: if (id.startsWith(QLatin1String("org.kde.kdeconnect_tp::"))) id = id.mid(id.indexOf(QLatin1String("::")) + 2); removeNotification(id); return; } QString id = np.get(QStringLiteral("id")); Notification* noti = nullptr; if (!m_internalIdToPublicId.contains(id)) { noti = new Notification(np, m_plugin->device(), this); if (noti->isReady()) { addNotification(noti); } else { connect(noti, &Notification::ready, this, &NotificationsDbusInterface::notificationReady); } } else { QString pubId = m_internalIdToPublicId.value(id); noti = m_notifications.value(pubId); } noti->update(np); } void NotificationsDbusInterface::addNotification(Notification* noti) { const QString& internalId = noti->internalId(); if (m_internalIdToPublicId.contains(internalId)) { removeNotification(internalId); } //qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "addNotification" << internalId; connect(noti, &Notification::dismissRequested, this, &NotificationsDbusInterface::dismissRequested); connect(noti, &Notification::replyRequested, this, [this,noti]{ replyRequested(noti); }); connect(noti, &Notification::actionTriggered, this, &NotificationsDbusInterface::sendAction); const QString& publicId = newId(); m_notifications[publicId] = noti; m_internalIdToPublicId[internalId] = publicId; - DbusHelper::sessionBus().registerObject(m_device->dbusPath()+"/notifications/"+publicId, noti, QDBusConnection::ExportScriptableContents); + DbusHelper::sessionBus().registerObject(m_device->dbusPath() + QStringLiteral("/notifications/") + publicId, noti, QDBusConnection::ExportScriptableContents); Q_EMIT notificationPosted(publicId); } void NotificationsDbusInterface::removeNotification(const QString& internalId) { //qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "removeNotification" << internalId; if (!m_internalIdToPublicId.contains(internalId)) { qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found noti by internal Id: " << internalId; return; } QString publicId = m_internalIdToPublicId.take(internalId); Notification* noti = m_notifications.take(publicId); if (!noti) { qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found noti by public Id: " << publicId; return; } //Deleting the notification will unregister it automatically //DbusHelper::sessionBus().unregisterObject(mDevice->dbusPath()+"/notifications/"+publicId); noti->deleteLater(); Q_EMIT notificationRemoved(publicId); } void NotificationsDbusInterface::dismissRequested(const QString& internalId) { NetworkPacket np(PACKET_TYPE_NOTIFICATION_REQUEST); np.set(QStringLiteral("cancel"), internalId); m_plugin->sendPacket(np); //Workaround: we erase notifications without waiting a response from the //phone because we won't receive a response if we are out of sync and this //notification no longer exists. Ideally, each time we reach the phone //after some time disconnected we should re-sync all the notifications. removeNotification(internalId); } void NotificationsDbusInterface::replyRequested(Notification* noti) { QString replyId = noti->replyId(); QString appName = noti->appName(); QString originalMessage = noti->ticker(); SendReplyDialog* dialog = new SendReplyDialog(originalMessage, replyId, appName); connect(dialog, &SendReplyDialog::sendReply, this, &NotificationsDbusInterface::sendReply); dialog->show(); dialog->raise(); } void NotificationsDbusInterface::sendReply(const QString& replyId, const QString& message) { NetworkPacket np(PACKET_TYPE_NOTIFICATION_REPLY); np.set(QStringLiteral("requestReplyId"), replyId); np.set(QStringLiteral("message"), message); m_plugin->sendPacket(np); } void NotificationsDbusInterface::sendAction(const QString& key, const QString& action) { NetworkPacket np(PACKET_TYPE_NOTIFICATION_ACTION); - np.set("key", key); - np.set("action", action); + np.set(QStringLiteral("key"), key); + np.set(QStringLiteral("action"), action); m_plugin->sendPacket(np); } QString NotificationsDbusInterface::newId() { return QString::number(++m_lastId); } diff --git a/plugins/notifications/notificationsplugin.cpp b/plugins/notifications/notificationsplugin.cpp index ca93863e..3fb979b8 100644 --- a/plugins/notifications/notificationsplugin.cpp +++ b/plugins/notifications/notificationsplugin.cpp @@ -1,64 +1,64 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "notificationsplugin.h" #include "notificationsdbusinterface.h" #include "notification_debug.h" #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_notifications.json", registerPlugin< NotificationsPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_NOTIFICATION, "kdeconnect.plugin.notification") NotificationsPlugin::NotificationsPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { notificationsDbusInterface = new NotificationsDbusInterface(this); } void NotificationsPlugin::connected() { - NetworkPacket np(PACKET_TYPE_NOTIFICATION_REQUEST, {{"request", true}}); + NetworkPacket np(PACKET_TYPE_NOTIFICATION_REQUEST, {{QStringLiteral("request"), true}}); sendPacket(np); } NotificationsPlugin::~NotificationsPlugin() { qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Destroying NotificationsPlugin"; //FIXME: Qt dbus does not allow to remove an adaptor! (it causes a crash in // the next dbus access to its parent). The implication of not deleting this // is that disabling the plugin leaks the interface. As a mitigation we are // cleaning up the notifications inside the adaptor here. //notificationsDbusInterface->deleteLater(); notificationsDbusInterface->clearNotifications(); } bool NotificationsPlugin::receivePacket(const NetworkPacket& np) { if (np.get(QStringLiteral("request"))) return false; notificationsDbusInterface->processPacket(np); return true; } #include "notificationsplugin.moc" diff --git a/plugins/notifications/sendreplydialog.cpp b/plugins/notifications/sendreplydialog.cpp index e957ba8e..4d960a4f 100644 --- a/plugins/notifications/sendreplydialog.cpp +++ b/plugins/notifications/sendreplydialog.cpp @@ -1,60 +1,60 @@ /** * Copyright 2015 Albert Vaca * * 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 . */ #include "sendreplydialog.h" #include #include #include #include #include #include "ui_sendreplydialog.h" SendReplyDialog::SendReplyDialog(const QString& originalMessage, const QString& replyId, const QString& topicName, QWidget* parent) : QDialog(parent) , m_replyId(replyId) , m_ui(new Ui::SendReplyDialog) { m_ui->setupUi(this); - m_ui->textView->setText(topicName + ": \n" + originalMessage); + m_ui->textView->setText(topicName + QStringLiteral(": \n") + originalMessage); auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok); button->setText(i18n("Send")); connect(this, &QDialog::accepted, this, &SendReplyDialog::sendButtonClicked); setWindowTitle(topicName); setWindowIcon(QIcon::fromTheme(QStringLiteral("kdeconnect"))); setAttribute(Qt::WA_DeleteOnClose); } SendReplyDialog::~SendReplyDialog() = default; void SendReplyDialog::sendButtonClicked() { Q_EMIT sendReply(m_replyId, m_ui->replyEdit->toPlainText()); close(); } QSize SendReplyDialog::sizeHint() const { return QSize(512, 64); } diff --git a/plugins/photo/photoplugin.cpp b/plugins/photo/photoplugin.cpp index f31f03bc..2260d77b 100644 --- a/plugins/photo/photoplugin.cpp +++ b/plugins/photo/photoplugin.cpp @@ -1,72 +1,72 @@ /** * Copyright 2019 Nicolas Fella * * 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 . */ #include "photoplugin.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_photo.json", registerPlugin< PhotoPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_PHOTO, "kdeconnect.plugin.photo") PhotoPlugin::PhotoPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } PhotoPlugin::~PhotoPlugin() { } bool PhotoPlugin::receivePacket(const NetworkPacket& np) { if (requestedFiles.isEmpty() || !np.hasPayload()) { return true; } const QString& fileName = requestedFiles.takeFirst(); FileTransferJob* job = np.createPayloadTransferJob(QUrl::fromLocalFile(fileName)); connect(job, &FileTransferJob::result, this, [this, fileName] { Q_EMIT photoReceived(fileName); }); job->start(); return true; } void PhotoPlugin::requestPhoto(const QString& fileName) { requestedFiles.append(fileName); NetworkPacket np(PACKET_TYPE_PHOTO_REQUEST); sendPacket(np); } QString PhotoPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/photo"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/photo"); } #include "photoplugin.moc" diff --git a/plugins/ping/pingplugin.cpp b/plugins/ping/pingplugin.cpp index 4207cfec..91e79c27 100644 --- a/plugins/ping/pingplugin.cpp +++ b/plugins/ping/pingplugin.cpp @@ -1,78 +1,78 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "pingplugin.h" #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_ping.json", registerPlugin< PingPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_PING, "kdeconnect.plugin.ping") PingPlugin::PingPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { // qCDebug(KDECONNECT_PLUGIN_PING) << "Ping plugin constructor for device" << device()->name(); } PingPlugin::~PingPlugin() { // qCDebug(KDECONNECT_PLUGIN_PING) << "Ping plugin destructor for device" << device()->name(); } bool PingPlugin::receivePacket(const NetworkPacket& np) { Daemon::instance()->sendSimpleNotification(QStringLiteral("pingReceived"), device()->name(), np.get(QStringLiteral("message"),i18n("Ping!")), QStringLiteral("dialog-ok")); return true; } void PingPlugin::sendPing() { NetworkPacket np(PACKET_TYPE_PING); bool success = sendPacket(np); qCDebug(KDECONNECT_PLUGIN_PING) << "sendPing:" << success; } void PingPlugin::sendPing(const QString& customMessage) { NetworkPacket np(PACKET_TYPE_PING); if (!customMessage.isEmpty()) { np.set(QStringLiteral("message"), customMessage); } bool success = sendPacket(np); qCDebug(KDECONNECT_PLUGIN_PING) << "sendPing:" << success; } QString PingPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/ping"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/ping"); } #include "pingplugin.moc" diff --git a/plugins/remotecommands/remotecommandsplugin.cpp b/plugins/remotecommands/remotecommandsplugin.cpp index 163cd154..4796b2f1 100644 --- a/plugins/remotecommands/remotecommandsplugin.cpp +++ b/plugins/remotecommands/remotecommandsplugin.cpp @@ -1,87 +1,87 @@ /** * Copyright 2016 Aleix Pol Gonzalez * * 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 . */ #include "remotecommandsplugin.h" #include #include #include #include #define PACKET_TYPE_RUNCOMMAND_REQUEST QLatin1String("kdeconnect.runcommand.request") K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_remotecommands.json", registerPlugin< RemoteCommandsPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_REMOTECOMMANDS, "kdeconnect.plugin.remotecommands") RemoteCommandsPlugin::RemoteCommandsPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_commands("{}") , m_canAddCommand(false) { } RemoteCommandsPlugin::~RemoteCommandsPlugin() = default; bool RemoteCommandsPlugin::receivePacket(const NetworkPacket& np) { if (np.has(QStringLiteral("commandList"))) { m_canAddCommand = np.get(QStringLiteral("canAddCommand")); setCommands(np.get(QStringLiteral("commandList"))); return true; } return false; } void RemoteCommandsPlugin::connected() { - NetworkPacket np(PACKET_TYPE_RUNCOMMAND_REQUEST, {{"requestCommandList", true}}); + NetworkPacket np(PACKET_TYPE_RUNCOMMAND_REQUEST, {{QStringLiteral("requestCommandList"), true}}); sendPacket(np); } QString RemoteCommandsPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/remotecommands"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/remotecommands"); } void RemoteCommandsPlugin::setCommands(const QByteArray& cmds) { if (m_commands != cmds) { m_commands = cmds; Q_EMIT commandsChanged(m_commands); } } void RemoteCommandsPlugin::triggerCommand(const QString& key) { - NetworkPacket np(PACKET_TYPE_RUNCOMMAND_REQUEST, {{ "key", key }}); + NetworkPacket np(PACKET_TYPE_RUNCOMMAND_REQUEST, {{QStringLiteral("key"), key }}); sendPacket(np); } void RemoteCommandsPlugin::editCommands() { - NetworkPacket np(PACKET_TYPE_RUNCOMMAND_REQUEST, {{ "setup", true }}); + NetworkPacket np(PACKET_TYPE_RUNCOMMAND_REQUEST, {{QStringLiteral("setup"), true }}); sendPacket(np); } #include "remotecommandsplugin.moc" diff --git a/plugins/remotecontrol/remotecontrolplugin.cpp b/plugins/remotecontrol/remotecontrolplugin.cpp index 0e348a1a..939f7d28 100644 --- a/plugins/remotecontrol/remotecontrolplugin.cpp +++ b/plugins/remotecontrol/remotecontrolplugin.cpp @@ -1,65 +1,65 @@ /** * Copyright 2015 Aleix Pol Gonzalez * * 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 . */ #include "remotecontrolplugin.h" #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_remotecontrol.json", registerPlugin< RemoteControlPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_REMOTECONTROL, "kdeconnect.plugin.remotecontrol") RemoteControlPlugin::RemoteControlPlugin(QObject* parent, const QVariantList &args) : KdeConnectPlugin(parent, args) { } RemoteControlPlugin::~RemoteControlPlugin() {} void RemoteControlPlugin::moveCursor(const QPoint &p) { NetworkPacket np(PACKET_TYPE_MOUSEPAD_REQUEST, { - {"dx", p.x()}, - {"dy", p.y()} + {QStringLiteral("dx"), p.x()}, + {QStringLiteral("dy"), p.y()} }); sendPacket(np); } void RemoteControlPlugin::sendCommand(const QString &name, bool val) { NetworkPacket np(PACKET_TYPE_MOUSEPAD_REQUEST, {{name, val}}); sendPacket(np); } QString RemoteControlPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/remotecontrol"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/remotecontrol"); } #include "remotecontrolplugin.moc" diff --git a/plugins/remotekeyboard/remotekeyboardplugin.cpp b/plugins/remotekeyboard/remotekeyboardplugin.cpp index a9c59ee6..9ac8272a 100644 --- a/plugins/remotekeyboard/remotekeyboardplugin.cpp +++ b/plugins/remotekeyboard/remotekeyboardplugin.cpp @@ -1,148 +1,148 @@ /** * Copyright 2017 Holger Kaelberer * * 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 . */ #include "remotekeyboardplugin.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_remotekeyboard.json", registerPlugin< RemoteKeyboardPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_REMOTEKEYBOARD, "kdeconnect.plugin.remotekeyboard"); // Mapping of Qt::Key to internal codes, corresponds to the mapping in mousepadplugin QMap specialKeysMap = { //0, // Invalid {Qt::Key_Backspace, 1}, {Qt::Key_Tab, 2}, //XK_Linefeed, // 3 {Qt::Key_Left, 4}, {Qt::Key_Up, 5}, {Qt::Key_Right, 6}, {Qt::Key_Down, 7}, {Qt::Key_PageUp, 8}, {Qt::Key_PageDown, 9}, {Qt::Key_Home, 10}, {Qt::Key_End, 11}, {Qt::Key_Return, 12}, {Qt::Key_Enter, 12}, {Qt::Key_Delete, 13}, {Qt::Key_Escape, 14}, {Qt::Key_SysReq, 15}, {Qt::Key_ScrollLock, 16}, //0, // 17 //0, // 18 //0, // 19 //0, // 20 {Qt::Key_F1, 21}, {Qt::Key_F2, 22}, {Qt::Key_F3, 23}, {Qt::Key_F4, 24}, {Qt::Key_F5, 25}, {Qt::Key_F6, 26}, {Qt::Key_F7, 27}, {Qt::Key_F8, 28}, {Qt::Key_F9, 29}, {Qt::Key_F10, 30}, {Qt::Key_F11, 31}, {Qt::Key_F12, 32}, }; RemoteKeyboardPlugin::RemoteKeyboardPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_remoteState(false) { } RemoteKeyboardPlugin::~RemoteKeyboardPlugin() { } bool RemoteKeyboardPlugin::receivePacket(const NetworkPacket& np) { if (np.type() == PACKET_TYPE_MOUSEPAD_ECHO) { - if (!np.has("isAck") || !np.has("key")) { + if (!np.has(QStringLiteral("isAck")) || !np.has(QStringLiteral("key"))) { qCWarning(KDECONNECT_PLUGIN_REMOTEKEYBOARD) << "Invalid packet of type" << PACKET_TYPE_MOUSEPAD_ECHO; return false; } // qCWarning(KDECONNECT_PLUGIN_REMOTEKEYBOARD) << "Received keypress" << np; - Q_EMIT keyPressReceived(np.get("key"), - np.get("specialKey", 0), - np.get("shift", false), - np.get("ctrl", false), - np.get("alt", false)); + Q_EMIT keyPressReceived(np.get(QStringLiteral("key")), + np.get(QStringLiteral("specialKey"), 0), + np.get(QStringLiteral("shift"), false), + np.get(QStringLiteral("ctrl"), false), + np.get(QStringLiteral("alt"), 0)); return true; } else if (np.type() == PACKET_TYPE_MOUSEPAD_KEYBOARDSTATE) { // qCWarning(KDECONNECT_PLUGIN_REMOTEKEYBOARD) << "Received keyboardstate" << np; - if (m_remoteState != np.get("state")) { - m_remoteState = np.get("state"); + if (m_remoteState != np.get(QStringLiteral("state"))) { + m_remoteState = np.get(QStringLiteral("state")); Q_EMIT remoteStateChanged(m_remoteState); } return true; } return false; } void RemoteKeyboardPlugin::sendKeyPress(const QString& key, int specialKey, bool shift, bool ctrl, bool alt, bool sendAck) const { NetworkPacket np(PACKET_TYPE_MOUSEPAD_REQUEST, { - {"key", key}, - {"specialKey", specialKey}, - {"shift", shift}, - {"ctrl", ctrl}, - {"alt", alt}, - {"sendAck", sendAck} + {QStringLiteral("key"), key}, + {QStringLiteral("specialKey"), specialKey}, + {QStringLiteral("shift"), shift}, + {QStringLiteral("ctrl"), ctrl}, + {QStringLiteral("alt"), alt}, + {QStringLiteral("sendAck"), sendAck} }); sendPacket(np); } void RemoteKeyboardPlugin::sendQKeyEvent(const QVariantMap& keyEvent, bool sendAck) const { - if (!keyEvent.contains("key")) + if (!keyEvent.contains(QStringLiteral("key"))) return; - int k = translateQtKey(keyEvent.value("key").toInt()); - int modifiers = keyEvent.value("modifiers").toInt(); - sendKeyPress(keyEvent.value("text").toString(), k, + int k = translateQtKey(keyEvent.value(QStringLiteral("key")).toInt()); + int modifiers = keyEvent.value(QStringLiteral("modifiers")).toInt(); + sendKeyPress(keyEvent.value(QStringLiteral("text")).toString(), k, modifiers & Qt::ShiftModifier, modifiers & Qt::ControlModifier, modifiers & Qt::AltModifier, sendAck); } int RemoteKeyboardPlugin::translateQtKey(int qtKey) const { return specialKeysMap.value(qtKey, 0); } void RemoteKeyboardPlugin::connected() { } QString RemoteKeyboardPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/remotekeyboard"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/remotekeyboard"); } #include "remotekeyboardplugin.moc" diff --git a/plugins/remotesystemvolume/remotesystemvolumeplugin.cpp b/plugins/remotesystemvolume/remotesystemvolumeplugin.cpp index 6b579024..6eafa8b6 100644 --- a/plugins/remotesystemvolume/remotesystemvolumeplugin.cpp +++ b/plugins/remotesystemvolume/remotesystemvolumeplugin.cpp @@ -1,105 +1,105 @@ /** * Copyright 2018 Nicolas Fella * * 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 . */ #include "remotesystemvolumeplugin.h" #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_remotesystemvolume.json", registerPlugin< RemoteSystemVolumePlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_PING, "kdeconnect.plugin.remotesystemvolume") RemoteSystemVolumePlugin::RemoteSystemVolumePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } RemoteSystemVolumePlugin::~RemoteSystemVolumePlugin() { } bool RemoteSystemVolumePlugin::receivePacket(const NetworkPacket& np) { if (np.has(QStringLiteral("sinkList"))) { QJsonDocument document(np.get(QStringLiteral("sinkList"))); m_sinks = document.toJson(); Q_EMIT sinksChanged(); } else { QString name = np.get(QStringLiteral("name")); if (np.has(QStringLiteral("volume"))) { Q_EMIT volumeChanged(name, np.get(QStringLiteral("volume"))); } if (np.has(QStringLiteral("muted"))) { Q_EMIT mutedChanged(name, np.get(QStringLiteral("muted"))); } } return true; } void RemoteSystemVolumePlugin::sendVolume(const QString& name, int volume) { NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME_REQUEST); np.set(QStringLiteral("name"), name); np.set(QStringLiteral("volume"), volume); sendPacket(np); } void RemoteSystemVolumePlugin::sendMuted(const QString& name, bool muted) { NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME_REQUEST); np.set(QStringLiteral("name"), name); np.set(QStringLiteral("muted"), muted); sendPacket(np); } void RemoteSystemVolumePlugin::connected() { NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME_REQUEST); np.set(QStringLiteral("requestSinks"), true); sendPacket(np); } QByteArray RemoteSystemVolumePlugin::sinks() { return m_sinks; } QString RemoteSystemVolumePlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/remotesystemvolume"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/remotesystemvolume"); } #include "remotesystemvolumeplugin.moc" diff --git a/plugins/runcommand/runcommandplugin.cpp b/plugins/runcommand/runcommandplugin.cpp index f32f6dfc..e962fd56 100644 --- a/plugins/runcommand/runcommandplugin.cpp +++ b/plugins/runcommand/runcommandplugin.cpp @@ -1,111 +1,111 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "runcommandplugin.h" #include #include #include #include #include #include #include #include #include #include #include #define PACKET_TYPE_RUNCOMMAND QStringLiteral("kdeconnect.runcommand") #ifdef Q_OS_WIN #define COMMAND "cmd" #define ARGS "/c" #else #define COMMAND "/bin/sh" #define ARGS "-c" #endif K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_runcommand.json", registerPlugin< RunCommandPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_RUNCOMMAND, "kdeconnect.plugin.runcommand") RunCommandPlugin::RunCommandPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { connect(config(), &KdeConnectPluginConfig::configChanged, this, &RunCommandPlugin::configChanged); } RunCommandPlugin::~RunCommandPlugin() { } bool RunCommandPlugin::receivePacket(const NetworkPacket& np) { if (np.get(QStringLiteral("requestCommandList"), false)) { sendConfig(); return true; } if (np.has(QStringLiteral("key"))) { QJsonDocument commandsDocument = QJsonDocument::fromJson(config()->get(QStringLiteral("commands"), "{}")); QJsonObject commands = commandsDocument.object(); QString key = np.get(QStringLiteral("key")); QJsonValue value = commands[key]; if (value == QJsonValue::Undefined) { qCWarning(KDECONNECT_PLUGIN_RUNCOMMAND) << key << "is not a configured command"; } const QJsonObject commandJson = value.toObject(); qCInfo(KDECONNECT_PLUGIN_RUNCOMMAND) << "Running:" << COMMAND << ARGS << commandJson[QStringLiteral("command")].toString(); QProcess::startDetached(QStringLiteral(COMMAND), QStringList()<< QStringLiteral(ARGS) << commandJson[QStringLiteral("command")].toString()); return true; - } else if (np.has("setup")) { + } else if (np.has(QStringLiteral("setup"))) { QProcess::startDetached(QStringLiteral("kcmshell5"), {QStringLiteral("kdeconnect"), QStringLiteral("--args"), QString(device()->id() + QStringLiteral(":kdeconnect_runcommand")) }); } return false; } void RunCommandPlugin::connected() { sendConfig(); } void RunCommandPlugin::sendConfig() { QString commands = config()->get(QStringLiteral("commands"),QStringLiteral("{}")); - NetworkPacket np(PACKET_TYPE_RUNCOMMAND, {{"commandList", commands}}); + NetworkPacket np(PACKET_TYPE_RUNCOMMAND, {{QStringLiteral("commandList"), commands}}); #if KCMUTILS_VERSION >= QT_VERSION_CHECK(5, 45, 0) np.set(QStringLiteral("canAddCommand"), true); #endif sendPacket(np); } void RunCommandPlugin::configChanged() { sendConfig(); } #include "runcommandplugin.moc" diff --git a/plugins/screensaver-inhibit/screensaverinhibitplugin.cpp b/plugins/screensaver-inhibit/screensaverinhibitplugin.cpp index 775517c5..0f9b9615 100644 --- a/plugins/screensaver-inhibit/screensaverinhibitplugin.cpp +++ b/plugins/screensaver-inhibit/screensaverinhibitplugin.cpp @@ -1,82 +1,82 @@ /** * Copyright 2014 Pramod Dematagoda * * 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 . */ #include "screensaverinhibitplugin.h" #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_screensaver_inhibit.json", registerPlugin< ScreensaverInhibitPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SCREENSAVERINHIBIT, "kdeconnect.plugin.screensaverinhibit") #define INHIBIT_SERVICE QStringLiteral("org.freedesktop.ScreenSaver") #define INHIBIT_INTERFACE INHIBIT_SERVICE #define INHIBIT_PATH QStringLiteral("/ScreenSaver") #define INHIBIT_METHOD QStringLiteral("Inhibit") #define UNINHIBIT_METHOD QStringLiteral("UnInhibit") #define SIMULATE_ACTIVITY_METHOD QStringLiteral("SimulateUserActivity") ScreensaverInhibitPlugin::ScreensaverInhibitPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { QDBusInterface inhibitInterface(INHIBIT_SERVICE, INHIBIT_PATH, INHIBIT_INTERFACE); QDBusMessage reply = inhibitInterface.call(INHIBIT_METHOD, QStringLiteral("org.kde.kdeconnect.daemon"), i18n("Phone is connected")); - if (reply.errorMessage() != nullptr) { + if (!reply.errorMessage().isEmpty()) { qCDebug(KDECONNECT_PLUGIN_SCREENSAVERINHIBIT) << "Unable to inhibit the screensaver: " << reply.errorMessage(); inhibitCookie = 0; } else { // Store the cookie we receive, this will be sent back when sending the uninhibit call. inhibitCookie = reply.arguments().at(0).toUInt(); } } ScreensaverInhibitPlugin::~ScreensaverInhibitPlugin() { if (inhibitCookie == 0) return; QDBusInterface inhibitInterface(INHIBIT_SERVICE, INHIBIT_PATH, INHIBIT_INTERFACE); inhibitInterface.call(UNINHIBIT_METHOD, this->inhibitCookie); /* * Simulate user activity because what ever manages the screensaver does not seem to start the timer * automatically when all inhibitions are lifted and the user does nothing which results in an * unlocked desktop which would be dangerous. Ideally we should not be doing this and the screen should * be locked anyway. */ inhibitInterface.call(SIMULATE_ACTIVITY_METHOD); } void ScreensaverInhibitPlugin::connected() { } bool ScreensaverInhibitPlugin::receivePacket(const NetworkPacket& np) { Q_UNUSED(np); return false; } #include "screensaverinhibitplugin.moc" diff --git a/plugins/sendnotifications/notificationslistener.cpp b/plugins/sendnotifications/notificationslistener.cpp index b954c6dd..250953a4 100644 --- a/plugins/sendnotifications/notificationslistener.cpp +++ b/plugins/sendnotifications/notificationslistener.cpp @@ -1,278 +1,278 @@ /** * Copyright 2015 Holger Kaelberer * * 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 . */ #include "notificationslistener.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sendnotificationsplugin.h" #include "sendnotification_debug.h" #include "notifyingapplication.h" //In older Qt released, qAsConst isnt available #include "qtcompat_p.h" NotificationsListener::NotificationsListener(KdeConnectPlugin* aPlugin) : QDBusAbstractAdaptor(aPlugin), m_plugin(aPlugin) { qRegisterMetaTypeStreamOperators("NotifyingApplication"); bool ret = DbusHelper::sessionBus() .registerObject(QStringLiteral("/org/freedesktop/Notifications"), this, QDBusConnection::ExportScriptableContents); if (!ret) qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Error registering notifications listener for device" << m_plugin->device()->name() << ":" << DbusHelper::sessionBus().lastError(); else qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Registered notifications listener for device" << m_plugin->device()->name(); QDBusInterface iface(QStringLiteral("org.freedesktop.DBus"), QStringLiteral("/org/freedesktop/DBus"), QStringLiteral("org.freedesktop.DBus")); iface.call(QStringLiteral("AddMatch"), - "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + QStringLiteral("interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'")); setTranslatedAppName(); loadApplications(); connect(m_plugin->config(), &KdeConnectPluginConfig::configChanged, this, &NotificationsListener::loadApplications); } NotificationsListener::~NotificationsListener() { qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Destroying NotificationsListener"; QDBusInterface iface(QStringLiteral("org.freedesktop.DBus"), QStringLiteral("/org/freedesktop/DBus"), QStringLiteral("org.freedesktop.DBus")); QDBusMessage res = iface.call(QStringLiteral("RemoveMatch"), - "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + QStringLiteral("interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'")); DbusHelper::sessionBus().unregisterObject(QStringLiteral("/org/freedesktop/Notifications")); } void NotificationsListener::setTranslatedAppName() { QString filePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("knotifications5/kdeconnect.notifyrc"), QStandardPaths::LocateFile); if (filePath.isEmpty()) { qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Couldn't find kdeconnect.notifyrc to hide kdeconnect notifications on the devices. Using default name."; m_translatedAppName = QStringLiteral("KDE Connect"); return; } KConfig config(filePath, KConfig::OpenFlag::SimpleConfig); KConfigGroup globalgroup(&config, QStringLiteral("Global")); m_translatedAppName = globalgroup.readEntry(QStringLiteral("Name"), QStringLiteral("KDE Connect")); } void NotificationsListener::loadApplications() { m_applications.clear(); const QVariantList list = m_plugin->config()->getList(QStringLiteral("applications")); for (const auto& a : list) { NotifyingApplication app = a.value(); if (!m_applications.contains(app.name)) m_applications.insert(app.name, app); } //qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Loaded" << applications.size() << " applications"; } bool NotificationsListener::parseImageDataArgument(const QVariant& argument, int& width, int& height, int& rowStride, int& bitsPerSample, int& channels, bool& hasAlpha, QByteArray& imageData) const { if (!argument.canConvert()) return false; const QDBusArgument dbusArg = argument.value(); dbusArg.beginStructure(); dbusArg >> width >> height >> rowStride >> hasAlpha >> bitsPerSample >> channels >> imageData; dbusArg.endStructure(); return true; } QSharedPointer NotificationsListener::iconForImageData(const QVariant& argument) const { int width, height, rowStride, bitsPerSample, channels; bool hasAlpha; QByteArray imageData; if (!parseImageDataArgument(argument, width, height, rowStride, bitsPerSample, channels, hasAlpha, imageData)) return QSharedPointer(); if (bitsPerSample != 8) { qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Unsupported image format:" << "width=" << width << "height=" << height << "rowStride=" << rowStride << "bitsPerSample=" << bitsPerSample << "channels=" << channels << "hasAlpha=" << hasAlpha; return QSharedPointer(); } QImage image(reinterpret_cast(imageData.data()), width, height, rowStride, hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32); if (hasAlpha) image = image.rgbSwapped(); // RGBA --> ARGB QSharedPointer buffer = QSharedPointer(new QBuffer); if (!buffer || !buffer->open(QIODevice::WriteOnly) || !image.save(buffer.data(), "PNG")) { qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Could not initialize image buffer"; return QSharedPointer(); } return buffer; } QSharedPointer NotificationsListener::iconForIconName(const QString& iconName) const { int size = KIconLoader::SizeEnormous; // use big size to allow for good // quality on high-DPI mobile devices QString iconPath = KIconLoader::global()->iconPath(iconName, -size, true); if (!iconPath.isEmpty()) { if (!iconPath.endsWith(QLatin1String(".png")) && KIconLoader::global()->theme()->name() != QLatin1String("hicolor")) { // try falling back to hicolor theme: KIconTheme hicolor(QStringLiteral("hicolor")); if (hicolor.isValid()) { - iconPath = hicolor.iconPath(iconName + ".png", size, KIconLoader::MatchBest); + iconPath = hicolor.iconPath(iconName + QStringLiteral(".png"), size, KIconLoader::MatchBest); //qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Found non-png icon in default theme trying fallback to hicolor:" << iconPath; } } } if (iconPath.endsWith(QLatin1String(".png"))) return QSharedPointer(new QFile(iconPath)); return QSharedPointer(); } uint NotificationsListener::Notify(const QString& appName, uint replacesId, const QString& appIcon, const QString& summary, const QString& body, const QStringList& actions, const QVariantMap& hints, int timeout) { static int id = 0; Q_UNUSED(actions); //qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Got notification appName=" << appName << "replacesId=" << replacesId << "appIcon=" << appIcon << "summary=" << summary << "body=" << body << "actions=" << actions << "hints=" << hints << "timeout=" << timeout; // skip our own notifications if (appName == m_translatedAppName) return 0; NotifyingApplication app; if (!m_applications.contains(appName)) { // new application -> add to config app.name = appName; app.icon = appIcon; app.active = true; app.blacklistExpression = QRegularExpression(); m_applications.insert(app.name, app); // update config: QVariantList list; for (const auto& a : qAsConst(m_applications)) list << QVariant::fromValue(a); m_plugin->config()->setList(QStringLiteral("applications"), list); //qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Added new application to config:" << app; } else app = m_applications.value(appName); if (!app.active) return 0; if (timeout > 0 && m_plugin->config()->get(QStringLiteral("generalPersistent"), false)) return 0; int urgency = -1; if (hints.contains(QStringLiteral("urgency"))) { bool ok; urgency = hints[QStringLiteral("urgency")].toInt(&ok); if (!ok) urgency = -1; } if (urgency > -1 && urgency < m_plugin->config()->get(QStringLiteral("generalUrgency"), 0)) return 0; QString ticker = summary; if (!body.isEmpty() && m_plugin->config()->get(QStringLiteral("generalIncludeBody"), true)) ticker += QStringLiteral(": ") + body; if (app.blacklistExpression.isValid() && !app.blacklistExpression.pattern().isEmpty() && app.blacklistExpression.match(ticker).hasMatch()) return 0; //qCDebug(KDECONNECT_PLUGIN_SENDNOTIFICATION) << "Sending notification from" << appName << ":" < 0 ? replacesId : ++id)}, - {"appName", appName}, - {"ticker", ticker}, - {"isClearable", timeout == 0} + {QStringLiteral("id"), QString::number(replacesId > 0 ? replacesId : ++id)}, + {QStringLiteral("appName"), appName}, + {QStringLiteral("ticker"), ticker}, + {QStringLiteral("isClearable"), timeout == 0} }); // KNotifications are persistent if // timeout == 0, for other notifications // clearability is pointless // sync any icon data? if (m_plugin->config()->get(QStringLiteral("generalSynchronizeIcons"), true)) { QSharedPointer iconSource; // try different image sources according to priorities in notifications- // spec version 1.2: if (hints.contains(QStringLiteral("image-data"))) iconSource = iconForImageData(hints[QStringLiteral("image-data")]); else if (hints.contains(QStringLiteral("image_data"))) // 1.1 backward compatibility iconSource = iconForImageData(hints[QStringLiteral("image_data")]); else if (hints.contains(QStringLiteral("image-path"))) iconSource = iconForIconName(hints[QStringLiteral("image-path")].toString()); else if (hints.contains(QStringLiteral("image_path"))) // 1.1 backward compatibility iconSource = iconForIconName(hints[QStringLiteral("image_path")].toString()); else if (!appIcon.isEmpty()) iconSource = iconForIconName(appIcon); else if (hints.contains(QStringLiteral("icon_data"))) // < 1.1 backward compatibility iconSource = iconForImageData(hints[QStringLiteral("icon_data")]); if (iconSource) np.setPayload(iconSource, iconSource->size()); } m_plugin->sendPacket(np); return (replacesId > 0 ? replacesId : id); } diff --git a/plugins/sftp/mounter.cpp b/plugins/sftp/mounter.cpp index 0241e7e8..9144e683 100644 --- a/plugins/sftp/mounter.cpp +++ b/plugins/sftp/mounter.cpp @@ -1,261 +1,261 @@ /** * Copyright 2014 Samoilenko Yuri * * 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 . */ #include "mounter.h" #include #include #include #include #include "mountloop.h" #include "config-sftp.h" #include "sftp_debug.h" #include "kdeconnectconfig.h" Mounter::Mounter(SftpPlugin* sftp) : QObject(sftp) , m_sftp(sftp) , m_proc(nullptr) , m_mountPoint(sftp->mountPoint()) , m_started(false) { connect(m_sftp, &SftpPlugin::packetReceived, this, &Mounter::onPackageReceived); connect(&m_connectTimer, &QTimer::timeout, this, &Mounter::onMountTimeout); connect(this, &Mounter::mounted, &m_connectTimer, &QTimer::stop); connect(this, &Mounter::failed, &m_connectTimer, &QTimer::stop); m_connectTimer.setInterval(10000); m_connectTimer.setSingleShot(true); QTimer::singleShot(0, this, &Mounter::start); qCDebug(KDECONNECT_PLUGIN_SFTP) << "Created mounter"; } Mounter::~Mounter() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Destroy mounter"; unmount(false); } bool Mounter::wait() { if (m_started) { return true; } qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting loop to wait for mount"; MountLoop loop; connect(this, &Mounter::mounted, &loop, &MountLoop::successed); connect(this, &Mounter::failed, &loop, &MountLoop::failed); return loop.exec(); } void Mounter::onPackageReceived(const NetworkPacket& np) { if (np.get(QStringLiteral("stop"), false)) { qCDebug(KDECONNECT_PLUGIN_SFTP) << "SFTP server stopped"; unmount(false); return; } - if (np.has("errorMessage")) { - Q_EMIT failed(np.get("errorMessage", "")); + if (np.has(QStringLiteral("errorMessage"))) { + Q_EMIT failed(np.get(QStringLiteral("errorMessage"))); return; } //This is the previous code, to access sftp server using KIO. Now we are //using the external binary sshfs, and accessing it as a local filesystem. /* * QUrl url; * url.setScheme("sftp"); * url.setHost(np.get("ip")); * url.setPort(np.get("port").toInt()); * url.setUserName(np.get("user")); * url.setPassword(np.get("password")); * url.setPath(np.get("path")); * new KRun(url, 0); * Q_EMIT mounted(); */ unmount(false); m_proc = new KProcess(); m_proc->setOutputChannelMode(KProcess::MergedChannels); connect(m_proc, &QProcess::started, this, &Mounter::onStarted); connect(m_proc, &QProcess::errorOccurred, this, &Mounter::onError); connect(m_proc, QOverload::of(&QProcess::finished), this, &Mounter::onFinished); QDir().mkpath(m_mountPoint); const QString program = QStringLiteral("sshfs"); QString path; - if (np.has(QStringLiteral("multiPaths"))) path = '/'; + if (np.has(QStringLiteral("multiPaths"))) path = QStringLiteral("/"); else path = np.get(QStringLiteral("path")); QHostAddress addr = m_sftp->device()->getLocalIpAddress(); if (addr == QHostAddress::Null) { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Device doesn't have a LanDeviceLink, unable to get IP address"; return; } QString ip = addr.toString(); const QStringList arguments = QStringList() << QStringLiteral("%1@%2:%3").arg( np.get(QStringLiteral("user")), ip, path) << m_mountPoint << QStringLiteral("-p") << np.get(QStringLiteral("port")) << QStringLiteral("-s") // This fixes a bug where file chunks are sent out of order and get corrupted on reception << QStringLiteral("-f") << QStringLiteral("-F") << QStringLiteral("/dev/null") //Do not use ~/.ssh/config - << QStringLiteral("-o") << "IdentityFile=" + KdeConnectConfig::instance()->privateKeyPath() + << QStringLiteral("-o") << QStringLiteral("IdentityFile=") + KdeConnectConfig::instance()->privateKeyPath() << QStringLiteral("-o") << QStringLiteral("StrictHostKeyChecking=no") //Do not ask for confirmation because it is not a known host << QStringLiteral("-o") << QStringLiteral("UserKnownHostsFile=/dev/null") //Prevent storing as a known host << QStringLiteral("-o") << QStringLiteral("HostKeyAlgorithms=+ssh-dss") //https://bugs.kde.org/show_bug.cgi?id=351725 << QStringLiteral("-o") << QStringLiteral("uid=") + QString::number(getuid()) << QStringLiteral("-o") << QStringLiteral("gid=") + QString::number(getgid()) << QStringLiteral("-o") << QStringLiteral("reconnect") << QStringLiteral("-o") << QStringLiteral("ServerAliveInterval=30") << QStringLiteral("-o") << QStringLiteral("password_stdin") ; m_proc->setProgram(program, arguments); qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting process: " << m_proc->program().join(QStringLiteral(" ")); m_proc->start(); //qCDebug(KDECONNECT_PLUGIN_SFTP) << "Passing password: " << np.get("password").toLatin1(); m_proc->write(np.get(QStringLiteral("password")).toLatin1()); m_proc->write("\n"); } void Mounter::onStarted() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process started"; m_started = true; Q_EMIT mounted(); //m_proc->setStandardOutputFile("/tmp/kdeconnect-sftp.out"); //m_proc->setStandardErrorFile("/tmp/kdeconnect-sftp.err"); auto proc = m_proc; connect(m_proc, &KProcess::readyReadStandardError, this, [proc]() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "stderr: " << proc->readAll(); }); connect(m_proc, &KProcess::readyReadStandardOutput, this, [proc]() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "stdout:" << proc->readAll(); }); } void Mounter::onError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process failed to start"; m_started = false; Q_EMIT failed(i18n("Failed to start sshfs")); } else if(error == QProcess::ProcessError::Crashed) { qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process crashed"; m_started = false; Q_EMIT failed(i18n("sshfs process crashed")); } else { qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process error" << error; m_started = false; Q_EMIT failed(i18n("Unknown error in sshfs")); } } void Mounter::onFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus == QProcess::NormalExit && exitCode == 0) { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process finished (exit code: " << exitCode << ")"; Q_EMIT unmounted(); } else { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process failed (exit code:" << exitCode << ")"; Q_EMIT failed(i18n("Error when accessing filesystem. sshfs finished with exit code %0").arg(exitCode)); } unmount(true); } void Mounter::onMountTimeout() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Timeout: device not responding"; Q_EMIT failed(i18n("Failed to mount filesystem: device not responding")); } void Mounter::start() { - NetworkPacket np(PACKET_TYPE_SFTP_REQUEST, {{"startBrowsing", true}}); + NetworkPacket np(PACKET_TYPE_SFTP_REQUEST, {{QStringLiteral("startBrowsing"), true}}); m_sftp->sendPacket(np); m_connectTimer.start(); } void Mounter::unmount(bool finished) { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Unmount" << m_proc; if (m_proc) { if (!finished) { //Process is still running, we want to stop it //But when the finished signal come, we might have already gone. //Disconnect everything. m_proc->disconnect(); m_proc->kill(); auto proc = m_proc; m_proc = nullptr; connect(proc, static_cast(&QProcess::finished), [proc]() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Free" << proc; proc->deleteLater(); }); Q_EMIT unmounted(); } else m_proc->deleteLater(); //Free mount point (won't always succeed if the path is in use) #if defined(HAVE_FUSERMOUNT) KProcess::execute(QStringList() << QStringLiteral("fusermount") << QStringLiteral("-u") << m_mountPoint, 10000); #else KProcess::execute(QStringList() << QStringLiteral("umount") << m_mountPoint, 10000); #endif m_proc = nullptr; } m_started = false; } diff --git a/plugins/sftp/sftpplugin.cpp b/plugins/sftp/sftpplugin.cpp index 28ac9087..b0e1d03d 100644 --- a/plugins/sftp/sftpplugin.cpp +++ b/plugins/sftp/sftpplugin.cpp @@ -1,201 +1,201 @@ /** * Copyright 2014 Samoilenko Yuri * * 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 . */ #include "sftpplugin.h" #include #include #include #include #include #include #include #include #include #include "mounter.h" #include "sftp_debug.h" K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_sftp.json", registerPlugin< SftpPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SFTP, "kdeconnect.plugin.sftp") static const QSet fields_c = QSet() << QStringLiteral("ip") << QStringLiteral("port") << QStringLiteral("user") << QStringLiteral("port") << QStringLiteral("path"); struct SftpPlugin::Pimpl { Pimpl() : m_mounter(nullptr) {} //Add KIO entry to Dolphin's Places KFilePlacesModel m_placesModel; Mounter* m_mounter; }; SftpPlugin::SftpPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , d(new Pimpl()) { deviceId = device()->id(); addToDolphin(); qCDebug(KDECONNECT_PLUGIN_SFTP) << "Created device:" << device()->name(); } SftpPlugin::~SftpPlugin() { removeFromDolphin(); unmount(); } void SftpPlugin::addToDolphin() { removeFromDolphin(); - QUrl kioUrl("kdeconnect://"+deviceId+"/"); + QUrl kioUrl(QStringLiteral("kdeconnect://") + deviceId + QStringLiteral("/")); d->m_placesModel.addPlace(device()->name(), kioUrl, QStringLiteral("kdeconnect")); qCDebug(KDECONNECT_PLUGIN_SFTP) << "add to dolphin"; } void SftpPlugin::removeFromDolphin() { - QUrl kioUrl("kdeconnect://"+deviceId+"/"); + QUrl kioUrl(QStringLiteral("kdeconnect://") + deviceId + QStringLiteral("/")); QModelIndex index = d->m_placesModel.closestItem(kioUrl); while (index.row() != -1) { d->m_placesModel.removePlace(index); index = d->m_placesModel.closestItem(kioUrl); } } void SftpPlugin::mount() { qCDebug(KDECONNECT_PLUGIN_SFTP) << "Mount device:" << device()->name(); if (d->m_mounter) { return; } d->m_mounter = new Mounter(this); connect(d->m_mounter, &Mounter::mounted, this, &SftpPlugin::onMounted); connect(d->m_mounter, &Mounter::unmounted, this, &SftpPlugin::onUnmounted); connect(d->m_mounter, &Mounter::failed, this, &SftpPlugin::onFailed); } void SftpPlugin::unmount() { if (d->m_mounter) { d->m_mounter->deleteLater(); d->m_mounter = nullptr; } } bool SftpPlugin::mountAndWait() { mount(); return d->m_mounter->wait(); } bool SftpPlugin::isMounted() const { return d->m_mounter && d->m_mounter->isMounted(); } QString SftpPlugin::getMountError() { if (!mountError.isEmpty()) { return mountError; } return QString(); } bool SftpPlugin::startBrowsing() { if (mountAndWait()) { //return new KRun(QUrl::fromLocalFile(mountPoint()), 0); - return new KRun(QUrl("kdeconnect://"+deviceId), nullptr); + return new KRun(QUrl(QStringLiteral("kdeconnect://") + deviceId), nullptr); } return false; } bool SftpPlugin::receivePacket(const NetworkPacket& np) { - if (!(fields_c - np.body().keys().toSet()).isEmpty() && !np.has("errorMessage")) { + if (!(fields_c - np.body().keys().toSet()).isEmpty() && !np.has(QStringLiteral("errorMessage"))) { // packet is invalid return false; } Q_EMIT packetReceived(np); remoteDirectories.clear(); if (np.has(QStringLiteral("multiPaths"))) { QStringList paths = np.get(QStringLiteral("multiPaths"),QStringList()); QStringList names = np.get(QStringLiteral("pathNames"),QStringList()); int size = qMin(names.size(), paths.size()); for (int i = 0; i < size; i++) { remoteDirectories.insert(mountPoint() + paths.at(i), names.at(i)); } } else { remoteDirectories.insert(mountPoint(), i18n("All files")); - remoteDirectories.insert(mountPoint() + "/DCIM/Camera", i18n("Camera pictures")); + remoteDirectories.insert(mountPoint() + QStringLiteral("/DCIM/Camera"), i18n("Camera pictures")); } return true; } QString SftpPlugin::mountPoint() { QString runtimePath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); if (runtimePath.isEmpty()) { runtimePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation); } return QDir(runtimePath).absoluteFilePath(deviceId); } void SftpPlugin::onMounted() { qCDebug(KDECONNECT_PLUGIN_SFTP) << device()->name() << QStringLiteral("Remote filesystem mounted at %1").arg(mountPoint()); Q_EMIT mounted(); } void SftpPlugin::onUnmounted() { qCDebug(KDECONNECT_PLUGIN_SFTP) << device()->name() << "Remote filesystem unmounted"; unmount(); Q_EMIT unmounted(); } void SftpPlugin::onFailed(const QString& message) { mountError = message; KNotification::event(KNotification::Error, device()->name(), message); unmount(); Q_EMIT unmounted(); } QVariantMap SftpPlugin::getDirectories() { return remoteDirectories; } #include "sftpplugin.moc" diff --git a/plugins/sftp/sftpplugin.h b/plugins/sftp/sftpplugin.h index 044368ea..389cf207 100644 --- a/plugins/sftp/sftpplugin.h +++ b/plugins/sftp/sftpplugin.h @@ -1,79 +1,79 @@ /** * Copyright 2014 Samoilenko Yuri * * 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 . */ #ifndef SFTPPLUGIN_H #define SFTPPLUGIN_H #include #include #define PACKET_TYPE_SFTP_REQUEST QStringLiteral("kdeconnect.sftp.request") class SftpPlugin : public KdeConnectPlugin { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.sftp") public: explicit SftpPlugin(QObject* parent, const QVariantList& args); ~SftpPlugin() override; bool receivePacket(const NetworkPacket& np) override; void connected() override {} - QString dbusPath() const override { return "/modules/kdeconnect/devices/" + deviceId + "/sftp"; } + QString dbusPath() const override { return QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/sftp"); } Q_SIGNALS: void packetReceived(const NetworkPacket& np); Q_SCRIPTABLE void mounted(); Q_SCRIPTABLE void unmounted(); public Q_SLOTS: Q_SCRIPTABLE void mount(); Q_SCRIPTABLE void unmount(); Q_SCRIPTABLE bool mountAndWait(); Q_SCRIPTABLE bool isMounted() const; Q_SCRIPTABLE QString getMountError(); Q_SCRIPTABLE bool startBrowsing(); Q_SCRIPTABLE QString mountPoint(); Q_SCRIPTABLE QVariantMap getDirectories(); //Actually a QMap, but QDBus prefers this private Q_SLOTS: void onMounted(); void onUnmounted(); void onFailed(const QString& message); private: void knotify(int type, const QString& text, const QPixmap& icon) const; void addToDolphin(); void removeFromDolphin(); private: struct Pimpl; QScopedPointer d; QString deviceId; //Storing it to avoid accessing device() from the destructor which could cause a crash QVariantMap remoteDirectories; //Actually a QMap, but QDBus prefers this QString mountError; }; #endif diff --git a/plugins/share/shareplugin.cpp b/plugins/share/shareplugin.cpp index 9f102c06..321b67a2 100644 --- a/plugins/share/shareplugin.cpp +++ b/plugins/share/shareplugin.cpp @@ -1,238 +1,238 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "shareplugin.h" #include "share_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core/filetransferjob.h" K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_share.json", registerPlugin< SharePlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SHARE, "kdeconnect.plugin.share") SharePlugin::SharePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_compositeJob() { } QUrl SharePlugin::destinationDir() const { const QString defaultDownloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); QUrl dir = QUrl::fromLocalFile(config()->get(QStringLiteral("incoming_path"), defaultDownloadPath)); if (dir.path().contains(QLatin1String("%1"))) { dir.setPath(dir.path().arg(device()->name())); } KJob* job = KIO::mkpath(dir); bool ret = job->exec(); if (!ret) { qWarning() << "couldn't create" << dir; } return dir; } QUrl SharePlugin::getFileDestination(const QString filename) const { const QUrl dir = destinationDir().adjusted(QUrl::StripTrailingSlash); QUrl destination(dir); - destination.setPath(dir.path() + '/' + filename, QUrl::DecodedMode); + destination.setPath(dir.path() + QStringLiteral("/") + filename, QUrl::DecodedMode); if (destination.isLocalFile() && QFile::exists(destination.toLocalFile())) { - destination.setPath(dir.path() + '/' + KIO::suggestName(dir, filename), QUrl::DecodedMode); + destination.setPath(dir.path() + QStringLiteral("/") + KIO::suggestName(dir, filename), QUrl::DecodedMode); } return destination; } static QString cleanFilename(const QString &filename) { int idx = filename.lastIndexOf(QLatin1Char('/')); return idx>=0 ? filename.mid(idx + 1) : filename; } void SharePlugin::setDateModified(const QUrl& destination, const qint64 timestamp) { QFile receivedFile(destination.toLocalFile()); if (!receivedFile.exists() || !receivedFile.open(QIODevice::ReadWrite | QIODevice::Text)) { return; } receivedFile.setFileTime(QDateTime::fromMSecsSinceEpoch(timestamp), QFileDevice::FileTime(QFileDevice::FileModificationTime)); } bool SharePlugin::receivePacket(const NetworkPacket& np) { /* //TODO: Write a test like this if (np.type() == PACKET_TYPE_PING) { qCDebug(KDECONNECT_PLUGIN_SHARE) << "sending file" << (QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.bashrc"); NetworkPacket out(PACKET_TYPE_SHARE_REQUEST); out.set("filename", mDestinationDir + "itworks.txt"); AutoClosingQFile* file = new AutoClosingQFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation) + "/.bashrc"); //Test file to transfer out.setPayload(file, file->size()); device()->sendPacket(out); return true; } */ qCDebug(KDECONNECT_PLUGIN_SHARE) << "File transfer"; if (np.hasPayload() || np.has(QStringLiteral("filename"))) { // qCDebug(KDECONNECT_PLUGIN_SHARE) << "receiving file" << filename << "in" << dir << "into" << destination; const QString filename = cleanFilename(np.get(QStringLiteral("filename"), QString::number(QDateTime::currentMSecsSinceEpoch()))); QUrl destination = getFileDestination(filename); if (np.hasPayload()) { qint64 dateModified = np.get(QStringLiteral("lastModified"), QDateTime::currentMSecsSinceEpoch()); if (!m_compositeJob) { m_compositeJob = new CompositeFileTransferJob(device()->id()); KIO::getJobTracker()->registerJob(m_compositeJob); } FileTransferJob* job = np.createPayloadTransferJob(destination); - job->setOriginName(device()->name() + ": " + filename); + job->setOriginName(device()->name() + QStringLiteral(": ") + filename); connect(job, &KJob::result, this, [this, dateModified] (KJob* job) -> void { finished(job, dateModified); }); m_compositeJob->addSubjob(job); if (!m_compositeJob->isRunning()) { m_compositeJob->start(); } } else { QFile file(destination.toLocalFile()); file.open(QIODevice::WriteOnly); file.close(); } } else if (np.has(QStringLiteral("text"))) { QString text = np.get(QStringLiteral("text")); KService::Ptr service = KMimeTypeTrader::self()->preferredService(QStringLiteral("text/plain")); const QString defaultApp = service ? service->desktopEntryName() : QString(); if (defaultApp == QLatin1String("org.kde.kate") || defaultApp == QLatin1String("org.kde.kwrite")) { QProcess* proc = new QProcess(); connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater())); - proc->start(defaultApp.section('.', 2,2), QStringList(QStringLiteral("--stdin"))); + proc->start(defaultApp.section(QStringLiteral("."), 2,2), QStringList(QStringLiteral("--stdin"))); proc->write(text.toUtf8()); proc->closeWriteChannel(); } else { QTemporaryFile tmpFile; tmpFile.setFileTemplate(QStringLiteral("kdeconnect-XXXXXX.txt")); tmpFile.setAutoRemove(false); tmpFile.open(); tmpFile.write(text.toUtf8()); tmpFile.close(); const QString fileName = tmpFile.fileName(); Q_EMIT shareReceived(fileName); QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)); } } else if (np.has(QStringLiteral("url"))) { QUrl url = QUrl::fromEncoded(np.get(QStringLiteral("url"))); QDesktopServices::openUrl(url); Q_EMIT shareReceived(url.toString()); } else { qCDebug(KDECONNECT_PLUGIN_SHARE) << "Error: Nothing attached!"; } return true; } void SharePlugin::finished(KJob* job, const qint64 dateModified) { FileTransferJob* ftjob = qobject_cast(job); if (ftjob && !job->error()) { Q_EMIT shareReceived(ftjob->destination().toString()); setDateModified(ftjob->destination(), dateModified); qCDebug(KDECONNECT_PLUGIN_SHARE) << "File transfer finished." << ftjob->destination(); } else { qCDebug(KDECONNECT_PLUGIN_SHARE) << "File transfer failed." << (ftjob ? ftjob->destination() : QUrl()); } } void SharePlugin::openDestinationFolder() { QDesktopServices::openUrl(destinationDir()); } void SharePlugin::shareUrl(const QUrl& url) { NetworkPacket packet(PACKET_TYPE_SHARE_REQUEST); if(url.isLocalFile()) { QSharedPointer ioFile(new QFile(url.toLocalFile())); packet.setPayload(ioFile, ioFile->size()); packet.set(QStringLiteral("filename"), QUrl(url).fileName()); } else { packet.set(QStringLiteral("url"), url.toString()); } sendPacket(packet); } void SharePlugin::shareUrls(const QStringList& urls) { for(const QString& url : urls) { shareUrl(QUrl(url)); } } void SharePlugin::shareText(const QString& text) { NetworkPacket packet(PACKET_TYPE_SHARE_REQUEST); packet.set(QStringLiteral("text"), text); sendPacket(packet); } void SharePlugin::openFile(const QUrl& url) { NetworkPacket packet(PACKET_TYPE_SHARE_REQUEST); if(url.isLocalFile()) { QSharedPointer ioFile(new QFile(url.toLocalFile())); packet.setPayload(ioFile, ioFile->size()); packet.set(QStringLiteral("filename"), QUrl(url).fileName()); packet.set(QStringLiteral("open"), true); } sendPacket(packet); } QString SharePlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/share"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/share"); } #include "shareplugin.moc" diff --git a/plugins/sms/smsplugin.cpp b/plugins/sms/smsplugin.cpp index 68773e79..03d7dec3 100644 --- a/plugins/sms/smsplugin.cpp +++ b/plugins/sms/smsplugin.cpp @@ -1,126 +1,126 @@ /** * Copyright 2013 Albert Vaca * Copyright 2018 Simon Redman * * 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 . */ #include "smsplugin.h" #include #include #include #include #include #include #include #include "sendreplydialog.h" K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_sms.json", registerPlugin< SmsPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SMS, "kdeconnect.plugin.sms") SmsPlugin::SmsPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_telepathyInterface(QStringLiteral("org.freedesktop.Telepathy.ConnectionManager.kdeconnect"), QStringLiteral("/kdeconnect")) , m_conversationInterface(new ConversationsDbusInterface(this)) { } SmsPlugin::~SmsPlugin() { // m_conversationInterface is self-deleting, see ~ConversationsDbusInterface for more information } bool SmsPlugin::receivePacket(const NetworkPacket& np) { if (np.type() == PACKET_TYPE_SMS_MESSAGES) { return handleBatchMessages(np); } return true; } void SmsPlugin::sendSms(const QString& phoneNumber, const QString& messageBody) { NetworkPacket np(PACKET_TYPE_SMS_REQUEST, { - {"sendSms", true}, - {"phoneNumber", phoneNumber}, - {"messageBody", messageBody} + {QStringLiteral("sendSms"), true}, + {QStringLiteral("phoneNumber"), phoneNumber}, + {QStringLiteral("messageBody"), messageBody} }); qCDebug(KDECONNECT_PLUGIN_SMS) << "Dispatching SMS send request to remote"; sendPacket(np); } void SmsPlugin::requestAllConversations() { NetworkPacket np(PACKET_TYPE_SMS_REQUEST_CONVERSATIONS); sendPacket(np); } void SmsPlugin::requestConversation (const qint64& conversationID) const { NetworkPacket np(PACKET_TYPE_SMS_REQUEST_CONVERSATION); - np.set("threadID", conversationID); + np.set(QStringLiteral("threadID"), conversationID); sendPacket(np); } void SmsPlugin::forwardToTelepathy(const ConversationMessage& message) { // If we don't have a valid Telepathy interface, bail out if (!(m_telepathyInterface.isValid())) return; qCDebug(KDECONNECT_PLUGIN_SMS) << "Passing a text message to the telepathy interface"; connect(&m_telepathyInterface, SIGNAL(messageReceived(QString,QString)), SLOT(sendSms(QString,QString)), Qt::UniqueConnection); const QString messageBody = message.body(); const QString contactName; // TODO: When telepathy support is improved, look up the contact with KPeople const QString phoneNumber = message.address(); m_telepathyInterface.call(QDBus::NoBlock, QStringLiteral("sendMessage"), phoneNumber, contactName, messageBody); } bool SmsPlugin::handleBatchMessages(const NetworkPacket& np) { - const auto messages = np.get("messages"); + const auto messages = np.get(QStringLiteral("messages")); QList messagesList; messagesList.reserve(messages.count()); for (const QVariant& body : messages) { ConversationMessage message(body.toMap()); if (message.containsTextBody()) { forwardToTelepathy(message); messagesList.append(message); } } m_conversationInterface->addMessages(messagesList); return true; } QString SmsPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/sms"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/sms"); } #include "smsplugin.moc" diff --git a/plugins/systemvolume/systemvolumeplugin-pulse.cpp b/plugins/systemvolume/systemvolumeplugin-pulse.cpp index 6f983927..a4e77abf 100644 --- a/plugins/systemvolume/systemvolumeplugin-pulse.cpp +++ b/plugins/systemvolume/systemvolumeplugin-pulse.cpp @@ -1,130 +1,130 @@ /** * Copyright 2017 Nicolas Fella * * 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 . */ #include "systemvolumeplugin-pulse.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_systemvolume.json", registerPlugin< SystemvolumePlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SYSTEMVOLUME, "kdeconnect.plugin.systemvolume") SystemvolumePlugin::SystemvolumePlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , sinksMap() {} bool SystemvolumePlugin::receivePacket(const NetworkPacket& np) { if (!PulseAudioQt::Context::instance()->isValid()) return false; if (np.has(QStringLiteral("requestSinks"))) { sendSinkList(); } else { QString name = np.get(QStringLiteral("name")); if (sinksMap.contains(name)) { if (np.has(QStringLiteral("volume"))) { sinksMap[name]->setVolume(np.get(QStringLiteral("volume"))); } if (np.has(QStringLiteral("muted"))) { sinksMap[name]->setMuted(np.get(QStringLiteral("muted"))); } } } return true; } void SystemvolumePlugin::sendSinkList() { QJsonDocument document; QJsonArray array; sinksMap.clear(); const auto sinks = PulseAudioQt::Context::instance()->sinks(); for (PulseAudioQt::Sink* sink : sinks) { sinksMap.insert(sink->name(), sink); connect(sink, &PulseAudioQt::Sink::volumeChanged, this, [this, sink] { NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); np.set(QStringLiteral("volume"), sink->volume()); np.set(QStringLiteral("name"), sink->name()); sendPacket(np); }); connect(sink, &PulseAudioQt::Sink::mutedChanged, this, [this, sink] { NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); np.set(QStringLiteral("muted"), sink->isMuted()); np.set(QStringLiteral("name"), sink->name()); sendPacket(np); }); QJsonObject sinkObject { - {"name", sink->name()}, - {"muted", sink->isMuted()}, - {"description", sink->description()}, - {"volume", sink->volume()}, - {"maxVolume", PulseAudioQt::normalVolume()} + {QStringLiteral("name"), sink->name()}, + {QStringLiteral("muted"), sink->isMuted()}, + {QStringLiteral("description"), sink->description()}, + {QStringLiteral("volume"), sink->volume()}, + {QStringLiteral("maxVolume"), PulseAudioQt::normalVolume()} }; array.append(sinkObject); } document.setArray(array); NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); np.set(QStringLiteral("sinkList"), document); sendPacket(np); } void SystemvolumePlugin::connected() { connect(PulseAudioQt::Context::instance(), &PulseAudioQt::Context::sinkAdded, this, [this] { sendSinkList(); }); connect(PulseAudioQt::Context::instance(), &PulseAudioQt::Context::sinkRemoved, this, [this] { sendSinkList(); }); const auto sinks = PulseAudioQt::Context::instance()->sinks(); for (PulseAudioQt::Sink* sink : sinks) { sinksMap.insert(sink->name(), sink); } } #include "systemvolumeplugin-pulse.moc" diff --git a/plugins/systemvolume/systemvolumeplugin-win.cpp b/plugins/systemvolume/systemvolumeplugin-win.cpp index d01cf585..8a8916e9 100644 --- a/plugins/systemvolume/systemvolumeplugin-win.cpp +++ b/plugins/systemvolume/systemvolumeplugin-win.cpp @@ -1,348 +1,348 @@ /** * Copyright 2018 Jun Bo Bi * * 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 . */ #include "systemvolumeplugin-win.h" #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KdeConnectPluginFactory, "kdeconnect_systemvolume.json", registerPlugin();) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SYSTEMVOLUME, "kdeconnect.plugin.systemvolume") // Private classes of SystemvolumePlugin class SystemvolumePlugin::CMMNotificationClient : public IMMNotificationClient { public: CMMNotificationClient(SystemvolumePlugin &x) : enclosing(x), _cRef(1){}; ~CMMNotificationClient(){}; // IUnknown methods -- AddRef, Release, and QueryInterface ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&_cRef); } ULONG STDMETHODCALLTYPE Release() override { ULONG ulRef = InterlockedDecrement(&_cRef); if (ulRef == 0) { delete this; } return ulRef; } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown *)this; } else if (__uuidof(IMMNotificationClient) == riid) { AddRef(); *ppvInterface = (IMMNotificationClient *)this; } else { *ppvInterface = NULL; return E_NOINTERFACE; } return S_OK; } // Callback methods for device-event notifications. HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) override { if (flow == eRender) { enclosing.sendSinkList(); } return S_OK; } HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) override { enclosing.sendSinkList(); return S_OK; } HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) override { enclosing.sendSinkList(); return S_OK; } HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) override { enclosing.sendSinkList(); return S_OK; } HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) override { enclosing.sendSinkList(); return S_OK; } private: LONG _cRef; SystemvolumePlugin &enclosing; }; class SystemvolumePlugin::CAudioEndpointVolumeCallback : public IAudioEndpointVolumeCallback { LONG _cRef; public: CAudioEndpointVolumeCallback(SystemvolumePlugin &x, QString sinkName) : enclosing(x), name(sinkName), _cRef(1) {} ~CAudioEndpointVolumeCallback(){}; // IUnknown methods -- AddRef, Release, and QueryInterface ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&_cRef); } ULONG STDMETHODCALLTYPE Release() override { ULONG ulRef = InterlockedDecrement(&_cRef); if (ulRef == 0) { delete this; } return ulRef; } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown *)this; } else if (__uuidof(IMMNotificationClient) == riid) { AddRef(); *ppvInterface = (IMMNotificationClient *)this; } else { *ppvInterface = NULL; return E_NOINTERFACE; } return S_OK; } // Callback method for endpoint-volume-change notifications. HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify) override { NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); np.set(QStringLiteral("volume"), (int)(pNotify->fMasterVolume * 100)); np.set(QStringLiteral("muted"), pNotify->bMuted); np.set(QStringLiteral("name"), name); enclosing.sendPacket(np); return S_OK; } private: SystemvolumePlugin &enclosing; QString name; }; SystemvolumePlugin::SystemvolumePlugin(QObject *parent, const QVariantList &args) : KdeConnectPlugin(parent, args), sinkList() { CoInitialize(nullptr); deviceEnumerator = nullptr; HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&(deviceEnumerator)); valid = (hr == S_OK); if (!valid) { qWarning("Initialization failed: Failed to create MMDeviceEnumerator"); qWarning("Error Code: %lx", hr); } } SystemvolumePlugin::~SystemvolumePlugin() { if (valid) { deviceEnumerator->UnregisterEndpointNotificationCallback(deviceCallback); deviceEnumerator->Release(); deviceEnumerator = nullptr; } } bool SystemvolumePlugin::sendSinkList() { if (!valid) return false; QJsonDocument document; QJsonArray array; HRESULT hr; if (!sinkList.empty()) { for (auto const &sink : sinkList) { sink.first->UnregisterControlChangeNotify(sink.second); sink.first->Release(); sink.second->Release(); } sinkList.clear(); } IMMDeviceCollection *devices = nullptr; hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); if (hr != S_OK) { qWarning("Failed to Enumumerate AudioEndpoints"); qWarning("Error Code: %lx", hr); return false; } unsigned int deviceCount; devices->GetCount(&deviceCount); for (unsigned int i = 0; i < deviceCount; i++) { IMMDevice *device = nullptr; IPropertyStore *deviceProperties = nullptr; PROPVARIANT deviceProperty; QString name; QString desc; float volume; BOOL muted; IAudioEndpointVolume *endpoint = nullptr; CAudioEndpointVolumeCallback *callback; // Get Properties devices->Item(i, &device); device->OpenPropertyStore(STGM_READ, &deviceProperties); deviceProperties->GetValue(PKEY_Device_FriendlyName, &deviceProperty); name = QString::fromWCharArray(deviceProperty.pwszVal); //PropVariantClear(&deviceProperty); #ifndef __MINGW32__ deviceProperties->GetValue(PKEY_Device_DeviceDesc, &deviceProperty); desc = QString::fromWCharArray(deviceProperty.pwszVal); //PropVariantClear(&deviceProperty); #endif QJsonObject sinkObject; - sinkObject.insert("name", name); - sinkObject.insert("description", desc); + sinkObject.insert(QStringLiteral("name"), name); + sinkObject.insert(QStringLiteral("description"), desc); hr = device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void **)&endpoint); if (hr != S_OK) { qWarning() << "Failed to create IAudioEndpointVolume for device:" << name; qWarning("Error Code: %lx", hr); device->Release(); continue; } endpoint->GetMasterVolumeLevelScalar(&volume); endpoint->GetMute(&muted); - sinkObject.insert("muted", (bool)muted); - sinkObject.insert("volume", (qint64)(volume * 100)); - sinkObject.insert("maxVolume", (qint64)100); + sinkObject.insert(QStringLiteral("muted"), (bool)muted); + sinkObject.insert(QStringLiteral("volume"), (qint64)(volume * 100)); + sinkObject.insert(QStringLiteral("maxVolume"), (qint64)100); // Register Callback callback = new CAudioEndpointVolumeCallback(*this, name); sinkList[name] = qMakePair(endpoint, callback); endpoint->RegisterControlChangeNotify(callback); device->Release(); array.append(sinkObject); } devices->Release(); document.setArray(array); NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); np.set(QStringLiteral("sinkList"), document); sendPacket(np); return true; } void SystemvolumePlugin::connected() { if (!valid) return; deviceCallback = new CMMNotificationClient(*this); deviceEnumerator->RegisterEndpointNotificationCallback(deviceCallback); sendSinkList(); } bool SystemvolumePlugin::receivePacket(const NetworkPacket &np) { if (!valid) return false; if (np.has(QStringLiteral("requestSinks"))) { return sendSinkList(); } else { QString name = np.get(QStringLiteral("name")); if (sinkList.contains(name)) { if (np.has(QStringLiteral("volume"))) { sinkList[name].first->SetMasterVolumeLevelScalar((float)np.get(QStringLiteral("volume")) / 100, NULL); } if (np.has(QStringLiteral("muted"))) { sinkList[name].first->SetMute(np.get(QStringLiteral("muted")), NULL); } } } return true; } #include "systemvolumeplugin-win.moc" diff --git a/plugins/telephony/telephonyplugin.cpp b/plugins/telephony/telephonyplugin.cpp index 3ffcf675..7a450186 100644 --- a/plugins/telephony/telephonyplugin.cpp +++ b/plugins/telephony/telephonyplugin.cpp @@ -1,134 +1,134 @@ /** * Copyright 2013 Albert Vaca * Copyright 2018 Simon Redman * * 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 . */ #include "telephonyplugin.h" #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_telephony.json", registerPlugin< TelephonyPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_TELEPHONY, "kdeconnect.plugin.telephony") TelephonyPlugin::TelephonyPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } TelephonyPlugin::~TelephonyPlugin() { } KNotification* TelephonyPlugin::createNotification(const NetworkPacket& np) { const QString event = np.get(QStringLiteral("event")); const QString phoneNumber = np.get(QStringLiteral("phoneNumber"), i18n("unknown number")); const QString contactName = np.get(QStringLiteral("contactName"), phoneNumber); const QByteArray phoneThumbnail = QByteArray::fromBase64(np.get(QStringLiteral("phoneThumbnail"), "")); QString content, type, icon; KNotification::NotificationFlags flags = KNotification::CloseOnTimeout; const QString title = device()->name(); if (event == QLatin1String("ringing")) { type = QStringLiteral("callReceived"); icon = QStringLiteral("call-start"); content = i18n("Incoming call from %1", contactName); } else if (event == QLatin1String("missedCall")) { type = QStringLiteral("missedCall"); icon = QStringLiteral("call-start"); content = i18n("Missed call from %1", contactName); flags |= KNotification::Persistent; //Note that in Unity this generates a message box! } else if (event == QLatin1String("talking")) { return nullptr; } else { #ifndef NDEBUG return nullptr; #else type = QStringLiteral("callReceived"); icon = QStringLiteral("phone"); content = i18n("Unknown telephony event: %1", event); #endif } Q_EMIT callReceived(type, phoneNumber, contactName); qCDebug(KDECONNECT_PLUGIN_TELEPHONY) << "Creating notification with type:" << type; KNotification* notification = new KNotification(type, flags, this); if (!phoneThumbnail.isEmpty()) { QPixmap photo; photo.loadFromData(phoneThumbnail, "JPEG"); notification->setPixmap(photo); } else { notification->setIconName(icon); } notification->setComponentName(QStringLiteral("kdeconnect")); notification->setTitle(title); notification->setText(content); if (event == QLatin1String("ringing")) { notification->setActions( QStringList(i18n("Mute Call")) ); connect(notification, &KNotification::action1Activated, this, &TelephonyPlugin::sendMutePacket); } return notification; } bool TelephonyPlugin::receivePacket(const NetworkPacket& np) { if (np.get(QStringLiteral("isCancel"))) { //TODO: Clear the old notification return true; } const QString& event = np.get(QStringLiteral("event"), QStringLiteral("unknown")); // Handle old-style packets if (np.type() == PACKET_TYPE_TELEPHONY) { if (event == QLatin1String("sms")) { return false; } KNotification* n = createNotification(np); if (n != nullptr) n->sendEvent(); return true; } return true; } void TelephonyPlugin::sendMutePacket() { - NetworkPacket packet(PACKET_TYPE_TELEPHONY_REQUEST_MUTE, {{"action", "mute"}}); + NetworkPacket packet(PACKET_TYPE_TELEPHONY_REQUEST_MUTE, {{QStringLiteral("action"), QStringLiteral("mute")}}); sendPacket(packet); } QString TelephonyPlugin::dbusPath() const { - return "/modules/kdeconnect/devices/" + device()->id() + "/telephony"; + return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/telephony"); } #include "telephonyplugin.moc" diff --git a/runners/remotecommands/remotecommandsrunner.cpp b/runners/remotecommands/remotecommandsrunner.cpp index 884c3e15..cacdd79a 100644 --- a/runners/remotecommands/remotecommandsrunner.cpp +++ b/runners/remotecommands/remotecommandsrunner.cpp @@ -1,91 +1,91 @@ /*************************************************************************** * Copyright © 2018 Nicolas Fella * * * * 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 . * ***************************************************************************/ #include "remotecommandsrunner.h" #include #include "interfaces/dbusinterfaces.h" K_EXPORT_PLASMA_RUNNER(installer, RemoteCommandsRunner) RemoteCommandsRunner::RemoteCommandsRunner(QObject *parent, const QVariantList &args) : Plasma::AbstractRunner(parent, args) , m_daemonInterface(new DaemonDbusInterface) { Q_UNUSED(args) setObjectName(QStringLiteral("Run Commands")); setPriority(AbstractRunner::HighestPriority); } RemoteCommandsRunner::~RemoteCommandsRunner() { } void RemoteCommandsRunner::match(Plasma::RunnerContext &context) { QDBusReply devicesReply = m_daemonInterface.devices(true, true); if (devicesReply.isValid()) { const auto devices = devicesReply.value(); for (const QString& deviceId : devices) { DeviceDbusInterface deviceInterface(deviceId, this); - if(!deviceInterface.hasPlugin("kdeconnect_remotecommands")) { + if(!deviceInterface.hasPlugin(QStringLiteral("kdeconnect_remotecommands"))) { continue; } RemoteCommandsDbusInterface remoteCommandsInterface(deviceId, this); const auto cmds = QJsonDocument::fromJson(remoteCommandsInterface.commands()).object(); for (auto it = cmds.constBegin(), itEnd = cmds.constEnd(); it!=itEnd; ++it) { const QJsonObject cont = it->toObject(); const QString deviceName = deviceInterface.name(); const QString commandName = cont.value(QStringLiteral("name")).toString(); if (deviceName.contains(context.query(), Qt::CaseInsensitive) || commandName.contains(context.query(), Qt::CaseInsensitive)) { Plasma::QueryMatch match(this); match.setType(Plasma::QueryMatch::PossibleMatch); match.setId(it.key()); match.setIconName(QStringLiteral("kdeconnect")); - match.setText(deviceName + ": " + commandName); + match.setText(deviceName + QStringLiteral(": ") + commandName); match.setSubtext(cont.value(QStringLiteral("command")).toString()); - match.setData(deviceId + "$" + it.key()); + match.setData(deviceId + QStringLiteral("$") + it.key()); context.addMatch(match); } } } } } void RemoteCommandsRunner::run(const Plasma::RunnerContext &/*context*/, const Plasma::QueryMatch &match) { - RemoteCommandsDbusInterface remoteCommandsInterface(match.data().toString().split("$")[0], this); + RemoteCommandsDbusInterface remoteCommandsInterface(match.data().toString().split(QStringLiteral("$"))[0], this); - remoteCommandsInterface.triggerCommand(match.data().toString().split("$")[1]); + remoteCommandsInterface.triggerCommand(match.data().toString().split(QStringLiteral("$"))[1]); } #include "remotecommandsrunner.moc" diff --git a/smsapp/main.cpp b/smsapp/main.cpp index ba87513d..6445f30f 100644 --- a/smsapp/main.cpp +++ b/smsapp/main.cpp @@ -1,72 +1,72 @@ /** * Copyright (C) 2018 Aleix Pol Gonzalez * Copyright (C) 2018 Simon Redman * * 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 . */ #include "conversationmodel.h" #include "conversationlistmodel.h" #include "kdeconnect-version.h" #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { QApplication app(argc, argv); - KAboutData aboutData("org.kde.kdeconnect.sms", i18n("SMS Instant Messaging"), QStringLiteral(KDECONNECT_VERSION_STRING), i18n("KDE Connect SMS"), KAboutLicense::GPL, i18n("(c) 2018, Aleix Pol Gonzalez")); - aboutData.addAuthor(i18n("Aleix Pol Gonzalez"), {}, "aleixpol@kde.org"); - aboutData.addAuthor(i18n("Nicolas Fella"), {}, "nicolas.fella@gmx.de"); - aboutData.addAuthor(i18n("Simon Redman"), {}, "simon@ergotech.com"); + KAboutData aboutData(QStringLiteral("org.kde.kdeconnect.sms"), i18n("SMS Instant Messaging"), QStringLiteral(KDECONNECT_VERSION_STRING), i18n("KDE Connect SMS"), KAboutLicense::GPL, i18n("(c) 2018, Aleix Pol Gonzalez")); + aboutData.addAuthor(i18n("Aleix Pol Gonzalez"), {}, QStringLiteral("aleixpol@kde.org")); + aboutData.addAuthor(i18n("Nicolas Fella"), {}, QStringLiteral("nicolas.fella@gmx.de")); + aboutData.addAuthor(i18n("Simon Redman"), {}, QStringLiteral("simon@ergotech.com")); KAboutData::setApplicationData(aboutData); QString initialMessage; { QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addVersionOption(); parser.addHelpOption(); parser.addOption(QCommandLineOption(QStringLiteral("message"), i18n("Send a message"), i18n("message"))); parser.process(app); aboutData.processCommandLine(&parser); if (parser.isSet(QStringLiteral("message"))) { initialMessage = parser.value(QStringLiteral("message")); } } KDBusService service(KDBusService::Unique); qmlRegisterType("org.kde.kdeconnect.sms", 1, 0, "QSortFilterProxyModel"); qmlRegisterType("org.kde.kdeconnect.sms", 1, 0, "ConversationModel"); qmlRegisterType("org.kde.kdeconnect.sms", 1, 0, "ConversationListModel"); QQmlApplicationEngine engine; engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.rootContext()->setContextProperty(QStringLiteral("_initialMessage"), QVariant(initialMessage)); - engine.load(QUrl("qrc:/qml/main.qml")); + engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); return app.exec(); } diff --git a/smsapp/smshelper.cpp b/smsapp/smshelper.cpp index a20c0d72..eac16c44 100644 --- a/smsapp/smshelper.cpp +++ b/smsapp/smshelper.cpp @@ -1,116 +1,116 @@ /** * Copyright (C) 2019 Simon Redman * * 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 . */ #include "smshelper.h" #include #include #include Q_LOGGING_CATEGORY(KDECONNECT_SMS_SMSHELPER, "kdeconnect.sms.smshelper") bool SmsHelper::isPhoneNumberMatchCanonicalized(const QString& canonicalPhone1, const QString& canonicalPhone2) { if (canonicalPhone1.isEmpty() || canonicalPhone2.isEmpty()) { // The empty string is not a valid phone number so does not match anything return false; } // To decide if a phone number matches: // 1. Are they similar lengths? If two numbers are very different, probably one is junk data and should be ignored // 2. Is one a superset of the other? Phone number digits get more specific the further towards the end of the string, // so if one phone number ends with the other, it is probably just a more-complete version of the same thing const QString& longerNumber = canonicalPhone1.length() >= canonicalPhone2.length() ? canonicalPhone1 : canonicalPhone2; const QString& shorterNumber = canonicalPhone1.length() < canonicalPhone2.length() ? canonicalPhone1 : canonicalPhone2; const CountryCode& country = determineCountryCode(longerNumber); const bool shorterNumberIsShortCode = isShortCode(shorterNumber, country); const bool longerNumberIsShortCode = isShortCode(longerNumber, country); if ((shorterNumberIsShortCode && !longerNumberIsShortCode) || (!shorterNumberIsShortCode && longerNumberIsShortCode)) { // If only one of the numbers is a short code, they clearly do not match return false; } bool matchingPhoneNumber = longerNumber.endsWith(shorterNumber); return matchingPhoneNumber; } bool SmsHelper::isPhoneNumberMatch(const QString& phone1, const QString& phone2) { const QString& canonicalPhone1 = canonicalizePhoneNumber(phone1); const QString& canonicalPhone2 = canonicalizePhoneNumber(phone2); return isPhoneNumberMatchCanonicalized(canonicalPhone1, canonicalPhone2); } bool SmsHelper::isShortCode(const QString& phoneNumber, const SmsHelper::CountryCode& country) { // Regardless of which country this number belongs to, a number of length less than 6 is a "short code" if (phoneNumber.length() <= 6) { return true; } - if (country == CountryCode::Australia && phoneNumber.length() == 8 && phoneNumber.startsWith("19")) { + if (country == CountryCode::Australia && phoneNumber.length() == 8 && phoneNumber.startsWith(QStringLiteral("19"))) { return true; } if (country == CountryCode::CzechRepublic && phoneNumber.length() <= 9) { // This entry of the Wikipedia article is fairly poorly written, so it is not clear whether a // short code with length 7 should start with a 9. Leave it like this for now, upgrade as // we get more information return true; } return false; } SmsHelper::CountryCode SmsHelper::determineCountryCode(const QString& canonicalNumber) { // This is going to fall apart if someone has not entered a country code into their contact book // or if Android decides it can't be bothered to report the country code, but probably we will // be fine anyway - if (canonicalNumber.startsWith("41")) { + if (canonicalNumber.startsWith(QStringLiteral("41"))) { return CountryCode::Australia; } - if (canonicalNumber.startsWith("420")) { + if (canonicalNumber.startsWith(QStringLiteral("420"))) { return CountryCode::CzechRepublic; } // The only countries I care about for the current implementation are Australia and CzechRepublic // If we need to deal with further countries, we should probably find a library return CountryCode::Other; } QString SmsHelper::canonicalizePhoneNumber(const QString& phoneNumber) { QString toReturn(phoneNumber); - toReturn = toReturn.remove(' '); - toReturn = toReturn.remove('-'); - toReturn = toReturn.remove('('); - toReturn = toReturn.remove(')'); - toReturn = toReturn.remove('+'); - toReturn = toReturn.remove(QRegularExpression("^0*")); // Strip leading zeroes + toReturn = toReturn.remove(QStringLiteral(" ")); + toReturn = toReturn.remove(QStringLiteral("-")); + toReturn = toReturn.remove(QStringLiteral("(")); + toReturn = toReturn.remove(QStringLiteral(")")); + toReturn = toReturn.remove(QStringLiteral("+")); + toReturn = toReturn.remove(QRegularExpression(QStringLiteral("^0*"))); // Strip leading zeroes if (toReturn.length() == 0) { // If we have stripped away everything, assume this is a special number (and already canonicalized) return phoneNumber; } return toReturn; } diff --git a/tests/kdeconnectconfigtest.cpp b/tests/kdeconnectconfigtest.cpp index 02e3acbe..94975c69 100644 --- a/tests/kdeconnectconfigtest.cpp +++ b/tests/kdeconnectconfigtest.cpp @@ -1,94 +1,94 @@ /** * Copyright 2015 Vineet Garg * * 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 . */ #include "../core/kdeconnectconfig.h" #include /* * This class tests the working of kdeconnect config that certificate and key is generated and saved properly */ class KdeConnectConfigTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void addTrustedDevice(); /* void remoteCertificateTest(); */ void removeTrustedDevice(); private: KdeConnectConfig* kcc; }; void KdeConnectConfigTest::initTestCase() { kcc = KdeConnectConfig::instance(); } void KdeConnectConfigTest::addTrustedDevice() { kcc->addTrustedDevice(QStringLiteral("testdevice"), QStringLiteral("Test Device"), QStringLiteral("phone")); KdeConnectConfig::DeviceInfo devInfo = kcc->getTrustedDevice(QStringLiteral("testdevice")); - QCOMPARE(devInfo.deviceName, QString("Test Device")); - QCOMPARE(devInfo.deviceType, QString("phone")); + QCOMPARE(devInfo.deviceName, QStringLiteral("Test Device")); + QCOMPARE(devInfo.deviceType, QStringLiteral("phone")); } /* // This checks whether certificate is generated correctly and stored correctly or not void KdeConnectConfigTest::remoteCertificateTest() { QSslCertificate certificate = kcc->certificate(); // Using same certificate as of device QCOMPARE(certificate.serialNumber().toInt(0,16), 10); QCOMPARE(certificate.subjectInfo(QSslCertificate::SubjectInfo::CommonName).first(), kcc->deviceId()); QCOMPARE(certificate.subjectInfo(QSslCertificate::SubjectInfo::Organization).first(), QString("KDE")); QCOMPARE(certificate.subjectInfo(QSslCertificate::OrganizationalUnitName).first(), QString("Kde connect")); kcc->setDeviceProperty("testdevice","certificate", QString::fromLatin1(certificate.toPem())); KdeConnectConfig::DeviceInfo devInfo = kcc->getTrustedDevice("testdevice"); QSslCertificate devCertificate = QSslCertificate::fromData(devInfo.certificate.toLatin1()).first(); QCOMPARE(devCertificate.serialNumber().toInt(0,16), 10); QCOMPARE(devCertificate.subjectInfo(QSslCertificate::SubjectInfo::CommonName).first(), kcc->deviceId()); QCOMPARE(devCertificate.subjectInfo(QSslCertificate::SubjectInfo::Organization).first(), QString("KDE")); QCOMPARE(devCertificate.subjectInfo(QSslCertificate::OrganizationalUnitName).first(), QString("Kde connect")); } */ void KdeConnectConfigTest::removeTrustedDevice() { kcc->removeTrustedDevice(QStringLiteral("testdevice")); KdeConnectConfig::DeviceInfo devInfo = kcc->getTrustedDevice(QStringLiteral("testdevice")); - QCOMPARE(devInfo.deviceName, QString("unnamed")); - QCOMPARE(devInfo.deviceType, QString("unknown")); + QCOMPARE(devInfo.deviceName, QStringLiteral("unnamed")); + QCOMPARE(devInfo.deviceType, QStringLiteral("unknown")); } QTEST_GUILESS_MAIN(KdeConnectConfigTest) #include "kdeconnectconfigtest.moc" diff --git a/tests/lanlinkprovidertest.cpp b/tests/lanlinkprovidertest.cpp index 3589404a..3d0ed68a 100644 --- a/tests/lanlinkprovidertest.cpp +++ b/tests/lanlinkprovidertest.cpp @@ -1,458 +1,458 @@ /** * Copyright 2015 Vineet Garg * * 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 . */ // This class tests the behaviour of the class LanLinkProvider, be sure to kill process kdeconnectd to avoid any port binding issues #include "../core/backends/lan/lanlinkprovider.h" #include "../core/backends/lan/server.h" #include "../core/backends/lan/socketlinereader.h" #include "../core/kdeconnectconfig.h" #include #include #include #include #include #include #include /* * This class tests the working of LanLinkProvider under different conditions that when identity packet is received over TCP, over UDP and same when the device is paired. * It depends on KdeConnectConfig since LanLinkProvider internally uses it. */ class LanLinkProviderTest : public QObject { Q_OBJECT public: explicit LanLinkProviderTest() : m_lanLinkProvider(true) , m_server(nullptr) , m_reader(nullptr) , m_udpSocket(nullptr) { QStandardPaths::setTestModeEnabled(true); } public Q_SLOTS: void initTestCase(); void init(); void cleanup(); private Q_SLOTS: /** * Test that the LanLinkProvider will send an identity packet to a non-default port */ void testChangedUDPBroadcastPort(); /** * Test that the LanLinkProvider will receive an identity packet on a non-default port */ void testChangedUDPListenPort(); void pairedDeviceTcpPacketReceived(); void pairedDeviceUdpPacketReceived(); void unpairedDeviceTcpPacketReceived(); void unpairedDeviceUdpPacketReceived(); private: const int TEST_PORT = 8520; // Add some private fields here LanLinkProvider m_lanLinkProvider; Server* m_server; SocketLineReader* m_reader; QUdpSocket* m_udpSocket; QString m_identityPacket; // Attributes for test device QString m_deviceId; QString m_name; QCA::PrivateKey m_privateKey; QSslCertificate m_certificate; QSslCertificate generateCertificate(QString&, QCA::PrivateKey&); void addTrustedDevice(); void removeTrustedDevice(); void setSocketAttributes(QSslSocket* socket); void testIdentityPacket(QByteArray& identityPacket); void socketBindErrorFail(const QUdpSocket& socket); }; void LanLinkProviderTest::initTestCase() { removeTrustedDevice(); // Remove trusted device if left by chance by any test m_deviceId = QStringLiteral("testdevice"); m_name = QStringLiteral("Test Device"); m_privateKey = QCA::KeyGenerator().createRSA(2048); m_certificate = generateCertificate(m_deviceId, m_privateKey); m_identityPacket = QStringLiteral("{\"id\":1439365924847,\"type\":\"kdeconnect.identity\",\"body\":{\"deviceId\":\"testdevice\",\"deviceName\":\"Test Device\",\"protocolVersion\":6,\"deviceType\":\"phone\",\"tcpPort\":") + QString::number(TEST_PORT) + QStringLiteral("}}"); } void LanLinkProviderTest::init() { m_lanLinkProvider.onStart(); } void LanLinkProviderTest::cleanup() { m_lanLinkProvider.onStop(); } void LanLinkProviderTest::testChangedUDPBroadcastPort() { quint16 udpListenPort = LanLinkProvider::UDP_PORT; quint16 udpBroadcastPort = LanLinkProvider::UDP_PORT + 1; m_lanLinkProvider.onStop(); LanLinkProvider testlanLinkProvider(true, udpBroadcastPort, udpListenPort); testlanLinkProvider.onStart(); QUdpSocket mUdpServer; bool bindSuccessful = mUdpServer.bind(QHostAddress::LocalHost, udpBroadcastPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); if (!bindSuccessful) { socketBindErrorFail(mUdpServer); } QSignalSpy spy(&mUdpServer, SIGNAL(readyRead())); testlanLinkProvider.onNetworkChange(); QVERIFY2(!spy.isEmpty() || spy.wait(), "Did not receive UDP packet"); } void LanLinkProviderTest::testChangedUDPListenPort() { quint16 udpListenPort = LanLinkProvider::UDP_PORT + 1; quint16 udpBroadcastPort = LanLinkProvider::UDP_PORT; m_lanLinkProvider.onStop(); LanLinkProvider testlanLinkProvider(true, udpBroadcastPort, udpListenPort); testlanLinkProvider.onStart(); m_server = new Server(this); QUdpSocket testUdpSocket; m_server->listen(QHostAddress::LocalHost, TEST_PORT); QSignalSpy spy(m_server, SIGNAL(newConnection())); // Write an identity packet to udp socket here. We do not broadcast it here. qint64 bytesWritten = testUdpSocket.writeDatagram(m_identityPacket.toLatin1(), QHostAddress::LocalHost, udpListenPort); QCOMPARE(bytesWritten, m_identityPacket.size()); // In response to receiving an identity packet, the LanLinkProvider should try to open a TCP connection to us QVERIFY(!spy.isEmpty() || spy.wait()); QSslSocket* serverSocket = m_server->nextPendingConnection(); QVERIFY2(serverSocket != 0, "Server socket is null"); QVERIFY2(serverSocket->isOpen(), "Server socket already closed"); delete m_server; } void LanLinkProviderTest::pairedDeviceTcpPacketReceived() { quint16 udpListenPort = LanLinkProvider::UDP_PORT; quint16 udpBroadcastPort = LanLinkProvider::UDP_PORT + 1; m_lanLinkProvider.onStop(); LanLinkProvider testlanLinkProvider(true, udpBroadcastPort, udpListenPort); testlanLinkProvider.onStart(); KdeConnectConfig* kcc = KdeConnectConfig::instance(); addTrustedDevice(); QUdpSocket* mUdpServer = new QUdpSocket; bool bindSuccessful = mUdpServer->bind(QHostAddress::LocalHost, udpBroadcastPort, QUdpSocket::ShareAddress); if (!bindSuccessful) { socketBindErrorFail(*mUdpServer); } QSignalSpy spy(mUdpServer, SIGNAL(readyRead())); testlanLinkProvider.onNetworkChange(); QVERIFY(!spy.isEmpty() || spy.wait()); QByteArray datagram; datagram.resize(mUdpServer->pendingDatagramSize()); QHostAddress sender; mUdpServer->readDatagram(datagram.data(), datagram.size(), &sender); testIdentityPacket(datagram); QJsonDocument jsonDocument = QJsonDocument::fromJson(datagram); QJsonObject body = jsonDocument.object().value(QStringLiteral("body")).toObject(); int tcpPort = body.value(QStringLiteral("tcpPort")).toInt(); QSslSocket socket; QSignalSpy spy2(&socket, SIGNAL(connected())); socket.connectToHost(sender, tcpPort); QVERIFY(spy2.wait()); QVERIFY2(socket.isOpen(), "Socket disconnected immediately"); socket.write(m_identityPacket.toLatin1()); socket.waitForBytesWritten(2000); QSignalSpy spy3(&socket, SIGNAL(encrypted())); setSocketAttributes(&socket); socket.addCaCertificate(kcc->certificate()); socket.setPeerVerifyMode(QSslSocket::VerifyPeer); socket.setPeerVerifyName(kcc->name()); socket.startServerEncryption(); QVERIFY(spy3.wait()); QCOMPARE(socket.sslErrors().size(), 0); QVERIFY2(socket.isValid(), "Server socket disconnected"); QVERIFY2(socket.isEncrypted(), "Server socket not yet encrypted"); QVERIFY2(!socket.peerCertificate().isNull(), "Peer certificate is null"); removeTrustedDevice(); delete mUdpServer; } void LanLinkProviderTest::pairedDeviceUdpPacketReceived() { KdeConnectConfig* kcc = KdeConnectConfig::instance(); addTrustedDevice(); m_server = new Server(this); m_udpSocket = new QUdpSocket(this); m_server->listen(QHostAddress::LocalHost, TEST_PORT); QSignalSpy spy(m_server, SIGNAL(newConnection())); qint64 bytesWritten = m_udpSocket->writeDatagram(m_identityPacket.toLatin1(), QHostAddress::LocalHost, LanLinkProvider::UDP_PORT); // write an identity packet to udp socket here, we do not broadcast it here QCOMPARE(bytesWritten, m_identityPacket.size()); // We should have an incoming connection now, wait for incoming connection QVERIFY(!spy.isEmpty() || spy.wait()); QSslSocket* serverSocket = m_server->nextPendingConnection(); QVERIFY2(serverSocket != 0, "Server socket is null"); QVERIFY2(serverSocket->isOpen(), "Server socket already closed"); m_reader = new SocketLineReader(serverSocket, this); QSignalSpy spy2(m_reader, SIGNAL(readyRead())); QVERIFY(spy2.wait()); QByteArray receivedPacket = m_reader->readLine(); testIdentityPacket(receivedPacket); // Received identity packet from LanLinkProvider now start ssl QSignalSpy spy3(serverSocket, SIGNAL(encrypted())); QVERIFY(connect(serverSocket, static_cast(&QSslSocket::error), this, [](QAbstractSocket::SocketError error){ qDebug() << "error:" << error; })); setSocketAttributes(serverSocket); serverSocket->addCaCertificate(kcc->certificate()); serverSocket->setPeerVerifyMode(QSslSocket::VerifyPeer); serverSocket->setPeerVerifyName(kcc->deviceId()); serverSocket->startClientEncryption(); // Its TCP server. but SSL client QVERIFY(!serverSocket->isEncrypted()); spy3.wait(2000); qDebug() << "xxxxxxxxx" << serverSocket->sslErrors(); QCOMPARE(serverSocket->sslErrors().size(), 0); QVERIFY2(serverSocket->isValid(), "Server socket disconnected"); QVERIFY2(serverSocket->isEncrypted(), "Server socket not yet encrypted"); QVERIFY2(!serverSocket->peerCertificate().isNull(), "Peer certificate is null"); removeTrustedDevice(); delete m_server; delete m_udpSocket; } void LanLinkProviderTest::unpairedDeviceTcpPacketReceived() { quint16 udpListenPort = LanLinkProvider::UDP_PORT; quint16 udpBroadcastPort = LanLinkProvider::UDP_PORT + 1; m_lanLinkProvider.onStop(); LanLinkProvider testlanLinkProvider(true, udpBroadcastPort, udpListenPort); testlanLinkProvider.onStart(); QUdpSocket* mUdpServer = new QUdpSocket; bool bindSuccessful = mUdpServer->bind(QHostAddress::LocalHost, udpBroadcastPort, QUdpSocket::ShareAddress); if (!bindSuccessful) { socketBindErrorFail(*mUdpServer); } QSignalSpy spy(mUdpServer, SIGNAL(readyRead())); testlanLinkProvider.onNetworkChange(); QVERIFY(!spy.isEmpty() || spy.wait()); QByteArray datagram; datagram.resize(mUdpServer->pendingDatagramSize()); QHostAddress sender; mUdpServer->readDatagram(datagram.data(), datagram.size(), &sender); testIdentityPacket(datagram); QJsonDocument jsonDocument = QJsonDocument::fromJson(datagram); QJsonObject body = jsonDocument.object().value(QStringLiteral("body")).toObject(); int tcpPort = body.value(QStringLiteral("tcpPort")).toInt(); QSslSocket socket; QSignalSpy spy2(&socket, SIGNAL(connected())); socket.connectToHost(sender, tcpPort); QVERIFY(spy2.wait()); QVERIFY2(socket.isOpen(), "Socket disconnected immediately"); socket.write(m_identityPacket.toLatin1()); socket.waitForBytesWritten(2000); QSignalSpy spy3(&socket, SIGNAL(encrypted())); // We don't take care for sslErrors signal here, but signal will emit still we will get successful connection setSocketAttributes(&socket); socket.setPeerVerifyMode(QSslSocket::QueryPeer); socket.startServerEncryption(); QVERIFY(spy3.wait()); QVERIFY2(socket.isValid(), "Server socket disconnected"); QVERIFY2(socket.isEncrypted(), "Server socket not yet encrypted"); QVERIFY2(!socket.peerCertificate().isNull(), "Peer certificate is null"); delete mUdpServer; } void LanLinkProviderTest::unpairedDeviceUdpPacketReceived() { m_server = new Server(this); m_udpSocket = new QUdpSocket(this); m_server->listen(QHostAddress::LocalHost, TEST_PORT); QSignalSpy spy(m_server, &Server::newConnection); qint64 bytesWritten = m_udpSocket->writeDatagram(m_identityPacket.toLatin1(), QHostAddress::LocalHost, LanLinkProvider::UDP_PORT); // write an identity packet to udp socket here, we do not broadcast it here QCOMPARE(bytesWritten, m_identityPacket.size()); QVERIFY(!spy.isEmpty() || spy.wait()); QSslSocket* serverSocket = m_server->nextPendingConnection(); QVERIFY2(serverSocket != 0, "Server socket is null"); QVERIFY2(serverSocket->isOpen(), "Server socket already closed"); m_reader = new SocketLineReader(serverSocket, this); QSignalSpy spy2(m_reader, &SocketLineReader::readyRead); QVERIFY(spy2.wait()); QByteArray receivedPacket = m_reader->readLine(); QVERIFY2(!receivedPacket.isEmpty(), "Empty packet received"); testIdentityPacket(receivedPacket); // Received identity packet from LanLinkProvider now start ssl QSignalSpy spy3(serverSocket, SIGNAL(encrypted())); setSocketAttributes(serverSocket); serverSocket->setPeerVerifyMode(QSslSocket::QueryPeer); serverSocket->startClientEncryption(); // Its TCP server. but SSL client QVERIFY(spy3.wait()); QVERIFY2(serverSocket->isValid(), "Server socket disconnected"); QVERIFY2(serverSocket->isEncrypted(), "Server socket not yet encrypted"); QVERIFY2(!serverSocket->peerCertificate().isNull(), "Peer certificate is null"); delete m_server; delete m_udpSocket; } void LanLinkProviderTest::testIdentityPacket(QByteArray& identityPacket) { QJsonDocument jsonDocument = QJsonDocument::fromJson(identityPacket); QJsonObject jsonObject = jsonDocument.object(); QJsonObject body = jsonObject.value(QStringLiteral("body")).toObject(); - QCOMPARE(jsonObject.value("type").toString(), QString("kdeconnect.identity")); - QVERIFY2(body.contains("deviceName"), "Device name not found in identity packet"); - QVERIFY2(body.contains("deviceId"), "Device id not found in identity packet"); - QVERIFY2(body.contains("protocolVersion"), "Protocol version not found in identity packet"); - QVERIFY2(body.contains("deviceType"), "Device type not found in identity packet"); + QCOMPARE(jsonObject.value(QStringLiteral("type")).toString(), QStringLiteral("kdeconnect.identity")); + QVERIFY2(body.contains(QStringLiteral("deviceName")), "Device name not found in identity packet"); + QVERIFY2(body.contains(QStringLiteral("deviceId")), "Device id not found in identity packet"); + QVERIFY2(body.contains(QStringLiteral("protocolVersion")), "Protocol version not found in identity packet"); + QVERIFY2(body.contains(QStringLiteral("deviceType")), "Device type not found in identity packet"); } QSslCertificate LanLinkProviderTest::generateCertificate(QString& commonName, QCA::PrivateKey& privateKey) { QDateTime startTime = QDateTime::currentDateTime(); QDateTime endTime = startTime.addYears(10); QCA::CertificateInfo certificateInfo; certificateInfo.insert(QCA::CommonName,commonName); certificateInfo.insert(QCA::Organization,QStringLiteral("KDE")); certificateInfo.insert(QCA::OrganizationalUnit,QStringLiteral("Kde connect")); QCA::CertificateOptions certificateOptions(QCA::PKCS10); certificateOptions.setSerialNumber(10); certificateOptions.setInfo(certificateInfo); certificateOptions.setValidityPeriod(startTime, endTime); certificateOptions.setFormat(QCA::PKCS10); QSslCertificate certificate = QSslCertificate(QCA::Certificate(certificateOptions, privateKey).toPEM().toLatin1()); return certificate; } void LanLinkProviderTest::setSocketAttributes(QSslSocket* socket) { socket->setPrivateKey(QSslKey(m_privateKey.toPEM().toLatin1(), QSsl::Rsa)); socket->setLocalCertificate(m_certificate); } void LanLinkProviderTest::addTrustedDevice() { KdeConnectConfig* kcc = KdeConnectConfig::instance(); kcc->addTrustedDevice(m_deviceId, m_name, QStringLiteral("phone")); kcc->setDeviceProperty(m_deviceId, QStringLiteral("certificate"), QString::fromLatin1(m_certificate.toPem())); } void LanLinkProviderTest::removeTrustedDevice() { KdeConnectConfig* kcc = KdeConnectConfig::instance(); kcc->removeTrustedDevice(m_deviceId); } void LanLinkProviderTest::socketBindErrorFail(const QUdpSocket& socket) { QAbstractSocket::SocketError sockErr = socket.error(); // Refer to https://doc.qt.io/qt-5/qabstractsocket.html#SocketError-enum to decode socket error number QString errorMessage = QLatin1String("Failed to bind UDP socket with error "); - errorMessage = errorMessage + QMetaEnum::fromType().valueToKey(sockErr); + errorMessage = errorMessage + QString::fromLatin1(QMetaEnum::fromType().valueToKey(sockErr)); QFAIL(errorMessage.toLocal8Bit().data()); } QTEST_GUILESS_MAIN(LanLinkProviderTest) #include "lanlinkprovidertest.moc" diff --git a/tests/networkpackettests.cpp b/tests/networkpackettests.cpp index d6e1d323..09d57d78 100644 --- a/tests/networkpackettests.cpp +++ b/tests/networkpackettests.cpp @@ -1,95 +1,95 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "networkpackettests.h" #include "core/networkpacket.h" #include QTEST_GUILESS_MAIN(NetworkPacketTests); void NetworkPacketTests::initTestCase() { // Called before the first testfunction is executed } void NetworkPacketTests::networkPacketTest() { NetworkPacket np(QStringLiteral("com.test")); - np.set(QStringLiteral("hello"),"hola"); - QCOMPARE( (np.get("hello","bye")) , QString("hola") ); + np.set(QStringLiteral("hello"), QStringLiteral("hola")); + QCOMPARE( (np.get(QStringLiteral("hello"), QStringLiteral("bye"))) , QStringLiteral("hola") ); - np.set(QStringLiteral("hello"),""); - QCOMPARE( (np.get("hello","bye")) , QString("") ); + np.set(QStringLiteral("hello"), QString()); + QCOMPARE((np.get(QStringLiteral("hello"), QStringLiteral("bye"))) , QString()); np.body().remove(QStringLiteral("hello")); - QCOMPARE( (np.get("hello","bye")) , QString("bye") ); + QCOMPARE((np.get(QStringLiteral("hello"), QStringLiteral("bye"))) , QStringLiteral("bye")); - np.set(QStringLiteral("foo"), "bar"); + np.set(QStringLiteral("foo"), QStringLiteral("bar")); QByteArray ba = np.serialize(); //qDebug() << "Serialized packet:" << ba; NetworkPacket np2(QLatin1String("")); NetworkPacket::unserialize(ba,&np2); QCOMPARE( np.id(), np2.id() ); QCOMPARE( np.type(), np2.type() ); QCOMPARE( np.body(), np2.body() ); QByteArray json("{\"id\":\"123\",\"type\":\"test\",\"body\":{\"testing\":true}}"); //qDebug() << json; NetworkPacket::unserialize(json,&np2); - QCOMPARE( np2.id(), QString("123") ); - QCOMPARE( (np2.get("testing")), true ); - QCOMPARE( (np2.get("not_testing")), false ); - QCOMPARE( (np2.get("not_testing",true)), true ); + QCOMPARE( np2.id(), QStringLiteral("123") ); + QCOMPARE( (np2.get(QStringLiteral("testing"))), true ); + QCOMPARE( (np2.get(QStringLiteral("not_testing"))), false ); + QCOMPARE( (np2.get(QStringLiteral("not_testing"),true)), true ); //NetworkPacket::unserialize("this is not json",&np2); //QtTest::ignoreMessage(QtSystemMsg, "json_parser - syntax error found, forcing abort, Line 1 Column 0"); //QtTest::ignoreMessage(QtDebugMsg, "Unserialization error: 1 \"syntax error, unexpected string\""); } void NetworkPacketTests::networkPacketIdentityTest() { NetworkPacket np(QLatin1String("")); NetworkPacket::createIdentityPacket(&np); - QCOMPARE( np.get("protocolVersion", -1) , NetworkPacket::s_protocolVersion ); + QCOMPARE( np.get(QStringLiteral("protocolVersion"), -1) , NetworkPacket::s_protocolVersion ); QCOMPARE( np.type() , PACKET_TYPE_IDENTITY ); } void NetworkPacketTests::cleanupTestCase() { // Called after the last testfunction was executed } void NetworkPacketTests::init() { // Called before each testfunction is executed } void NetworkPacketTests::cleanup() { // Called after every testfunction } diff --git a/tests/pluginloadtest.cpp b/tests/pluginloadtest.cpp index 98f9cd89..864d6abd 100644 --- a/tests/pluginloadtest.cpp +++ b/tests/pluginloadtest.cpp @@ -1,92 +1,92 @@ /** * Copyright 2015 Aleix Pol Gonzalez * * 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 . */ #include #include #include #include #include #include #include #include #include "core/daemon.h" #include "core/device.h" #include "core/kdeconnectplugin.h" #include #include "kdeconnect-version.h" #include "testdaemon.h" class PluginLoadTest : public QObject { Q_OBJECT public: PluginLoadTest() { QStandardPaths::setTestModeEnabled(true); m_daemon = new TestDaemon; } private Q_SLOTS: void testPlugins() { if (!(m_daemon->getLinkProviders().size() > 0)) { QFAIL("No links available, but loopback should have been provided by the test"); } Device* d = nullptr; m_daemon->acquireDiscoveryMode(QStringLiteral("plugintest")); const QList devicesList = m_daemon->devicesList(); for (Device* id : devicesList) { if (id->isReachable()) { if (!id->isTrusted()) id->requestPair(); d = id; break; } } if (d == nullptr) { QFAIL("Unable to determine device"); } m_daemon->releaseDiscoveryMode(QStringLiteral("plugintest")); if (!d->loadedPlugins().contains(QStringLiteral("kdeconnect_remotecontrol"))) { QSKIP("kdeconnect_remotecontrol is required for this test"); } QVERIFY(d); QVERIFY(d->isTrusted()); QVERIFY(d->isReachable()); d->setPluginEnabled(QStringLiteral("kdeconnect_mousepad"), false); - QCOMPARE(d->isPluginEnabled("kdeconnect_mousepad"), false); - QVERIFY(d->supportedPlugins().contains("kdeconnect_remotecontrol")); + QCOMPARE(d->isPluginEnabled(QStringLiteral("kdeconnect_mousepad")), false); + QVERIFY(d->supportedPlugins().contains(QStringLiteral("kdeconnect_remotecontrol"))); d->setPluginEnabled(QStringLiteral("kdeconnect_mousepad"), true); - QCOMPARE(d->isPluginEnabled("kdeconnect_mousepad"), true); - QVERIFY(d->supportedPlugins().contains("kdeconnect_remotecontrol")); + QCOMPARE(d->isPluginEnabled(QStringLiteral("kdeconnect_mousepad")), true); + QVERIFY(d->supportedPlugins().contains(QStringLiteral("kdeconnect_remotecontrol"))); } private: TestDaemon* m_daemon; }; QTEST_MAIN(PluginLoadTest); #include "pluginloadtest.moc" diff --git a/tests/sendfiletest.cpp b/tests/sendfiletest.cpp index 892adc7c..02d87f23 100644 --- a/tests/sendfiletest.cpp +++ b/tests/sendfiletest.cpp @@ -1,160 +1,160 @@ /** * Copyright 2015 Aleix Pol Gonzalez * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include "core/daemon.h" #include "core/device.h" #include "core/kdeconnectplugin.h" #include #include "kdeconnect-version.h" #include "testdaemon.h" #include #include class TestSendFile : public QObject { Q_OBJECT public: TestSendFile() { QStandardPaths::setTestModeEnabled(true); m_daemon = new TestDaemon; } private Q_SLOTS: void testSend() { if (!(m_daemon->getLinkProviders().size() > 0)) { QFAIL("No links available, but loopback should have been provided by the test"); } m_daemon->acquireDiscoveryMode(QStringLiteral("test")); Device* d = nullptr; const QList devicesList = m_daemon->devicesList(); for (Device* id : devicesList) { if (id->isReachable()) { if (!id->isTrusted()) id->requestPair(); d = id; } } if (d == nullptr) { QFAIL("Unable to determine device"); } m_daemon->releaseDiscoveryMode(QStringLiteral("test")); QCOMPARE(d->isReachable(), true); QCOMPARE(d->isTrusted(), true); QByteArray content("12312312312313213123213123"); QTemporaryFile temp; temp.open(); temp.write(content); temp.close(); KdeConnectPlugin* plugin = d->plugin(QStringLiteral("kdeconnect_share")); QVERIFY(plugin); plugin->metaObject()->invokeMethod(plugin, "shareUrl", Q_ARG(QString, QUrl::fromLocalFile(temp.fileName()).toString())); QSignalSpy spy(plugin, SIGNAL(shareReceived(QString))); QVERIFY(spy.wait(2000)); QVariantList args = spy.takeFirst(); QUrl sentFile(args.first().toUrl()); QFile file(sentFile.toLocalFile()); QCOMPARE(file.size(), content.size()); QVERIFY(file.open(QIODevice::ReadOnly)); QCOMPARE(file.readAll(), content); } void testSslJobs() { const QString aFile = QFINDTESTDATA("sendfiletest.cpp"); - const QString destFile = QDir::tempPath() + "/kdeconnect-test-sentfile"; + const QString destFile = QDir::tempPath() + QStringLiteral("/kdeconnect-test-sentfile"); QFile(destFile).remove(); const QString deviceId = KdeConnectConfig::instance()->deviceId() , deviceName = QStringLiteral("testdevice") , deviceType = KdeConnectConfig::instance()->deviceType(); KdeConnectConfig* kcc = KdeConnectConfig::instance(); kcc->addTrustedDevice(deviceId, deviceName, deviceType); kcc->setDeviceProperty(deviceId, QStringLiteral("certificate"), QString::fromLatin1(kcc->certificate().toPem())); // Using same certificate from kcc, instead of generating //We need the device to be loaded on the daemon, otherwise CompositeUploadJob will get a null device Device* device = new Device(this, deviceId); m_daemon->addDevice(device); QSharedPointer f(new QFile(aFile)); NetworkPacket np(PACKET_TYPE_SHARE_REQUEST); np.setPayload(f, f->size()); CompositeUploadJob* job = new CompositeUploadJob(deviceId, false); UploadJob* uj = new UploadJob(np); job->addSubjob(uj); QSignalSpy spyUpload(job, &KJob::result); job->start(); f->open(QIODevice::ReadWrite); FileTransferJob* ft = np.createPayloadTransferJob(QUrl::fromLocalFile(destFile)); QSignalSpy spyTransfer(ft, &KJob::result); ft->start(); QVERIFY(spyTransfer.count() || spyTransfer.wait()); if (ft->error()) { qWarning() << "fterror" << ft->errorString(); } QCOMPARE(ft->error(), 0); QCOMPARE(spyUpload.count(), 1); QFile resultFile(destFile), originFile(aFile); QVERIFY(resultFile.open(QIODevice::ReadOnly)); QVERIFY(originFile.open(QIODevice::ReadOnly)); const QByteArray resultContents = resultFile.readAll(), originContents = originFile.readAll(); QCOMPARE(resultContents.size(), originContents.size()); QCOMPARE(resultFile.readAll(), originFile.readAll()); } private: TestDaemon* m_daemon; }; QTEST_MAIN(TestSendFile); #include "sendfiletest.moc" diff --git a/tests/testnotificationlistener.cpp b/tests/testnotificationlistener.cpp index 4897df6a..6de12a4f 100644 --- a/tests/testnotificationlistener.cpp +++ b/tests/testnotificationlistener.cpp @@ -1,456 +1,456 @@ /** * Copyright 2015 Holger Kaelberer * * 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 . */ #include #include #include #include #include #include #include #include #include #include "core/daemon.h" #include "core/device.h" #include "testdevice.h" #include "core/kdeconnectplugin.h" #include "kdeconnect-version.h" #include "plugins/sendnotifications/sendnotificationsplugin.h" #include "plugins/sendnotifications/notificationslistener.h" #include "plugins/sendnotifications/notifyingapplication.h" // Tweaked NotificationsPlugin for testing class TestNotificationsPlugin : public SendNotificationsPlugin { Q_OBJECT public: explicit TestNotificationsPlugin(QObject* parent, const QVariantList& args) : SendNotificationsPlugin(parent, args) { } ~TestNotificationsPlugin() override = default; // allow to access notificationsListener for testing: NotificationsListener* getNotificationsListener() const { return notificationsListener; } void setNotificationsListener(NotificationsListener* value) { notificationsListener = value; } }; // Tweaked NotificationsListener for testing: class TestedNotificationsListener: public NotificationsListener { Q_OBJECT public: explicit TestedNotificationsListener(KdeConnectPlugin* aPlugin) : NotificationsListener(aPlugin) {} ~TestedNotificationsListener() override {} QHash& getApplications() { return m_applications; } void setApplications(const QHash& value) { m_applications = value; } QSharedPointer iconForIconName(const QString& iconName) const { return NotificationsListener::iconForIconName(iconName); } protected: bool parseImageDataArgument(const QVariant& argument, int& width, int& height, int& rowStride, int& bitsPerSample, int& channels, bool& hasAlpha, QByteArray& imageData) const override { width = argument.toMap().value(QStringLiteral("width")).toInt(); height = argument.toMap().value(QStringLiteral("height")).toInt(); rowStride = argument.toMap().value(QStringLiteral("rowStride")).toInt(); bitsPerSample = argument.toMap().value(QStringLiteral("bitsPerSample")).toInt(); channels = argument.toMap().value(QStringLiteral("channels")).toInt(); hasAlpha = argument.toMap().value(QStringLiteral("hasAlpha")).toBool(); imageData = argument.toMap().value(QStringLiteral("imageData")).toByteArray(); return true; } }; class TestNotificationListener : public QObject { Q_OBJECT public: TestNotificationListener() : plugin(nullptr) { QStandardPaths::setTestModeEnabled(true); } private Q_SLOTS: void testNotify(); private: TestNotificationsPlugin* plugin; }; void TestNotificationListener::testNotify() { // // set things up: // QString dId(QStringLiteral("testid")); TestDevice* d = new TestDevice(nullptr, dId); int proxiedNotifications = 0; QCOMPARE(proxiedNotifications, d->getSentPackets()); plugin = new TestNotificationsPlugin(this, QVariantList({ QVariant::fromValue(d), - "notifications_plugin", - {"kdeconnect.notification"}, - "preferences-desktop-notification"})); + QStringLiteral("notifications_plugin"), + {QStringLiteral("kdeconnect.notification")}, + QStringLiteral("preferences-desktop-notification")})); QVERIFY(plugin->getNotificationsListener()); delete plugin->getNotificationsListener(); // inject our tweaked NotificationsListener: TestedNotificationsListener* listener = new TestedNotificationsListener(plugin); QVERIFY(listener); plugin->setNotificationsListener(listener); QCOMPARE(listener, plugin->getNotificationsListener()); // make sure config is default: plugin->config()->set(QStringLiteral("generalPersistent"), false); plugin->config()->set(QStringLiteral("generalIncludeBody"), true); plugin->config()->set(QStringLiteral("generalUrgency"), 0); - QCOMPARE(plugin->config()->get("generalPersistent"), false); - QCOMPARE(plugin->config()->get("generalIncludeBody"), true); - QCOMPARE(plugin->config()->get("generalUrgency"), false); + QCOMPARE(plugin->config()->get(QStringLiteral("generalPersistent")), false); + QCOMPARE(plugin->config()->get(QStringLiteral("generalIncludeBody")), true); + QCOMPARE(plugin->config()->get(QStringLiteral("generalUrgency")), false); // applications are modified directly: listener->getApplications().clear(); QCOMPARE(listener->getApplications().count(), 0); // // Go !!! // uint replacesId = 99; uint retId; QString appName(QStringLiteral("some-appName")); QString body(QStringLiteral("some-body")); QString icon(QStringLiteral("some-icon")); QString summary(QStringLiteral("some-summary")); // regular Notify call that is synchronized ... retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0); // ... should return replacesId, QCOMPARE(retId, replacesId); // ... have triggered sending a packet QCOMPARE(++proxiedNotifications, d->getSentPackets()); // ... with our properties, - QCOMPARE(d->getLastPacket()->get("id"), replacesId); - QCOMPARE(d->getLastPacket()->get("appName"), appName); - QCOMPARE(d->getLastPacket()->get("ticker"), summary + ": " + body); - QCOMPARE(d->getLastPacket()->get("isClearable"), true); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("id")), replacesId); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("appName")), appName); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("ticker")), summary + QStringLiteral(": ") + body); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("isClearable")), true); QCOMPARE(d->getLastPacket()->hasPayload(), false); // ... and create a new application internally that is initialized correctly: QCOMPARE(listener->getApplications().count(), 1); QVERIFY(listener->getApplications().contains(appName)); QVERIFY(listener->getApplications()[appName].active); QCOMPARE(listener->getApplications()[appName].name, appName); QVERIFY(listener->getApplications()[appName].blacklistExpression.pattern().isEmpty()); QCOMPARE(listener->getApplications()[appName].name, appName); QCOMPARE(listener->getApplications()[appName].icon, icon); // another one, with other timeout and urgency values: QString appName2(QStringLiteral("some-appName2")); QString body2(QStringLiteral("some-body2")); QString icon2(QStringLiteral("some-icon2")); QString summary2(QStringLiteral("some-summary2")); - retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{"urgency", 2}}, 10); + retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{QStringLiteral("urgency"), 2}}, 10); QCOMPARE(retId, replacesId+1); QCOMPARE(++proxiedNotifications, d->getSentPackets()); - QCOMPARE(d->getLastPacket()->get("id"), replacesId+1); - QCOMPARE(d->getLastPacket()->get("appName"), appName2); - QCOMPARE(d->getLastPacket()->get("ticker"), summary2 + ": " + body2); - QCOMPARE(d->getLastPacket()->get("isClearable"), false); // timeout != 0 + QCOMPARE(d->getLastPacket()->get(QStringLiteral("id")), replacesId+1); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("appName")), appName2); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("ticker")), summary2 + QStringLiteral(": ") + body2); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("isClearable")), false); // timeout != 0 QCOMPARE(d->getLastPacket()->hasPayload(), false); QCOMPARE(listener->getApplications().count(), 2); QVERIFY(listener->getApplications().contains(appName2)); QVERIFY(listener->getApplications().contains(appName)); // if persistent-only is set, timeouts > 0 are not synced: plugin->config()->set(QStringLiteral("generalPersistent"), true); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 1); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName2, replacesId, icon2, summary2, body2, {}, {{}}, 3); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // but timeout == 0 is retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); plugin->config()->set(QStringLiteral("generalPersistent"), false); // if min-urgency is set, lower urgency levels are not synced: plugin->config()->set(QStringLiteral("generalUrgency"), 1); - retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 0}}, 0); + retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{QStringLiteral("urgency"), 0}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // equal urgency is - retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 1}}, 0); + retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{QStringLiteral("urgency"), 1}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // higher urgency as well - retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 2}}, 0); + retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{QStringLiteral("urgency"), 2}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); plugin->config()->set(QStringLiteral("generalUrgency"), 0); // notifications for a deactivated application are not synced: QVERIFY(listener->getApplications().contains(appName)); listener->getApplications()[appName].active = false; QVERIFY(!listener->getApplications()[appName].active); - retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{"urgency", 0}}, 0); + retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{QStringLiteral("urgency"), 0}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // others are still: retId = listener->Notify(appName2, replacesId+1, icon2, summary2, body2, {}, {{}}, 0); QCOMPARE(retId, replacesId+1); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // back to normal: listener->getApplications()[appName].active = true; QVERIFY(listener->getApplications()[appName].active); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // notifications with blacklisted subjects are not synced: QVERIFY(listener->getApplications().contains(appName)); listener->getApplications()[appName].blacklistExpression.setPattern(QStringLiteral("black[12]|foo(bar|baz)")); retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary black1"), body, {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary foobar"), body, {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // other subjects are synced: retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary foo"), body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName, replacesId, icon, QStringLiteral("summary black3"), body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // also body is checked by blacklist if requested: plugin->config()->set(QStringLiteral("generalIncludeBody"), true); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body black1"), {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body foobaz"), {}, {{}}, 0); QCOMPARE(retId, 0U); QCOMPARE(proxiedNotifications, d->getSentPackets()); // body does not matter if inclusion was not requested: plugin->config()->set(QStringLiteral("generalIncludeBody"), false); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body black1"), {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // without body, also ticker value is different: - QCOMPARE(d->getLastPacket()->get("ticker"), summary); + QCOMPARE(d->getLastPacket()->get(QStringLiteral("ticker")), summary); retId = listener->Notify(appName, replacesId, icon, summary, QStringLiteral("body foobaz"), {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // back to normal: listener->getApplications()[appName].blacklistExpression.setPattern(QLatin1String("")); plugin->config()->set(QStringLiteral("generalIncludeBody"), true); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); retId = listener->Notify(appName2, replacesId, icon2, summary2, body2, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); // icon synchronization: QStringList iconPaths; // appIcon int count = 0; const QStringList icons = KIconLoader::global()->queryIcons(-KIconLoader::SizeEnormous, KIconLoader::Application); for (const auto& iconName : icons) { if (!iconName.endsWith(QLatin1String(".png"))) continue; if (count++ > 3) // max 3 iterations break; iconPaths.append(iconName); // memorize some paths for later // existing icons are sync-ed if requested plugin->config()->set(QStringLiteral("generalSynchronizeIcons"), true); QFileInfo fi(iconName); retId = listener->Notify(appName, replacesId, fi.baseName(), summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QCOMPARE(d->getLastPacket()->payloadSize(), listener->iconForIconName(fi.baseName())->size()); // works also with absolute paths retId = listener->Notify(appName, replacesId, iconName, summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QCOMPARE(d->getLastPacket()->payloadSize(), fi.size()); // extensions other than png are not accepted: - retId = listener->Notify(appName, replacesId, iconName + ".svg", summary, body, {}, {{}}, 0); + retId = listener->Notify(appName, replacesId, iconName + QStringLiteral(".svg"), summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(!d->getLastPacket()->hasPayload()); // if sync not requested no payload: plugin->config()->set(QStringLiteral("generalSynchronizeIcons"), false); retId = listener->Notify(appName, replacesId, fi.baseName(), summary, body, {}, {{}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(!d->getLastPacket()->hasPayload()); QCOMPARE(d->getLastPacket()->payloadSize(), 0); } plugin->config()->set(QStringLiteral("generalSynchronizeIcons"), true); // image-path in hints if (iconPaths.size() > 0) { - retId = listener->Notify(appName, replacesId, iconPaths.size() > 1 ? iconPaths[1] : icon, summary, body, {}, {{"image-path", iconPaths[0]}}, 0); + retId = listener->Notify(appName, replacesId, iconPaths.size() > 1 ? iconPaths[1] : icon, summary, body, {}, {{QStringLiteral("image-path"), iconPaths[0]}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QFileInfo hintsFi(iconPaths[0]); // image-path has priority over appIcon parameter: QCOMPARE(d->getLastPacket()->payloadSize(), hintsFi.size()); } // image_path in hints if (iconPaths.size() > 0) { - retId = listener->Notify(appName, replacesId, iconPaths.size() > 1 ? iconPaths[1] : icon, summary, body, {}, {{"image_path", iconPaths[0]}}, 0); + retId = listener->Notify(appName, replacesId, iconPaths.size() > 1 ? iconPaths[1] : icon, summary, body, {}, {{QStringLiteral("image_path"), iconPaths[0]}}, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); QFileInfo hintsFi(iconPaths[0]); // image_path has priority over appIcon parameter: QCOMPARE(d->getLastPacket()->payloadSize(), hintsFi.size()); } // image-data in hints // set up: QBuffer* buffer; QImage image; int width = 2, height = 2, rowStride = 4*width, bitsPerSample = 8, channels = 4; bool hasAlpha = 1; char rawData[] = { 0x01, 0x02, 0x03, 0x04, // raw rgba data 0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34 }; - QVariantMap imageData = {{"width", width}, {"height", height}, {"rowStride", rowStride}, - {"bitsPerSample", bitsPerSample}, {"channels", channels}, - {"hasAlpha", hasAlpha}, {"imageData", QByteArray(rawData, sizeof(rawData))}}; + QVariantMap imageData = {{QStringLiteral("width"), width}, {QStringLiteral("height"), height}, {QStringLiteral("rowStride"), rowStride}, + {QStringLiteral("bitsPerSample"), bitsPerSample}, {QStringLiteral("channels"), channels}, + {QStringLiteral("hasAlpha"), hasAlpha}, {QStringLiteral("imageData"), QByteArray(rawData, sizeof(rawData))}}; QVariantMap hints; #define COMPARE_PIXEL(x, y) \ QCOMPARE(qRed(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 0]); \ QCOMPARE(qGreen(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 1]); \ QCOMPARE(qBlue(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 2]); \ QCOMPARE(qAlpha(image.pixel(x,y)), (int)rawData[x*4 + y*rowStride + 3]); hints.insert(QStringLiteral("image-data"), imageData); if (iconPaths.size() > 0) hints.insert(QStringLiteral("image-path"), iconPaths[0]); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, hints, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); buffer = dynamic_cast(d->getLastPacket()->payload().data()); QCOMPARE(d->getLastPacket()->payloadSize(), buffer->size()); // image-data is attached as png data QVERIFY(image.loadFromData(reinterpret_cast(buffer->data().constData()), buffer->size(), "PNG")); // image-data has priority over image-path: QCOMPARE(image.byteCount(), rowStride*height); // rgba -> argb conversion was done correctly: COMPARE_PIXEL(0,0); COMPARE_PIXEL(1,0); COMPARE_PIXEL(0,1); COMPARE_PIXEL(1,1); // same for image_data in hints hints.clear(); hints.insert(QStringLiteral("image-data"), imageData); if (iconPaths.size() > 0) hints.insert(QStringLiteral("image_path"), iconPaths[0]); retId = listener->Notify(appName, replacesId, icon, summary, body, {}, hints, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()->hasPayload()); buffer = dynamic_cast(d->getLastPacket()->payload().data()); QCOMPARE(d->getLastPacket()->payloadSize(), buffer->size()); // image-data is attached as png data QVERIFY(image.loadFromData(reinterpret_cast(buffer->data().constData()), buffer->size(), "PNG")); // image_data has priority over image_path/image-path: QCOMPARE(image.byteCount(), rowStride*height); // rgba -> argb conversion was done correctly: COMPARE_PIXEL(0,0); COMPARE_PIXEL(1,0); COMPARE_PIXEL(0,1); COMPARE_PIXEL(1,1); // same for icon_data, which has lowest priority hints.clear(); hints.insert(QStringLiteral("icon_data"), imageData); retId = listener->Notify(appName, replacesId, QLatin1String(""), summary, body, {}, hints, 0); QCOMPARE(retId, replacesId); QCOMPARE(++proxiedNotifications, d->getSentPackets()); QVERIFY(d->getLastPacket()); QVERIFY(d->getLastPacket()->hasPayload()); buffer = dynamic_cast(d->getLastPacket()->payload().data()); // image-data is attached as png data QVERIFY(image.loadFromData(reinterpret_cast(buffer->data().constData()), buffer->size(), "PNG")); QCOMPARE(image.byteCount(), rowStride*height); // rgba -> argb conversion was done correctly: COMPARE_PIXEL(0,0); COMPARE_PIXEL(1,0); COMPARE_PIXEL(0,1); COMPARE_PIXEL(1,1); #undef COMPARE_PIXEL } QTEST_GUILESS_MAIN(TestNotificationListener); #include "testnotificationlistener.moc" diff --git a/urlhandler/kdeconnect-handler.cpp b/urlhandler/kdeconnect-handler.cpp index 2aec828f..b2ea5f1d 100644 --- a/urlhandler/kdeconnect-handler.cpp +++ b/urlhandler/kdeconnect-handler.cpp @@ -1,133 +1,133 @@ /* * Copyright 2017 Aleix Pol Gonzalez * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdeconnect-version.h" #include "ui_dialog.h" /** * Show only devices that can be shared to */ class ShareDevicesProxyModel : public DevicesSortProxyModel { Q_OBJECT public: bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override { const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); auto device = qobject_cast(idx.data(DevicesModel::DeviceRole).value()); return device->supportedPlugins().contains(QStringLiteral("kdeconnect_share")); } }; int main(int argc, char** argv) { QApplication app(argc, argv); const QString description = i18n("KDE Connect URL handler"); KAboutData about(QStringLiteral("kdeconnect-urlhandler"), description, QStringLiteral(KDECONNECT_VERSION_STRING), description, KAboutLicense::GPL, i18n("(C) 2017 Aleix Pol Gonzalez")); about.addAuthor( QStringLiteral("Aleix Pol Gonzalez"), QString(), QStringLiteral("aleixpol@kde.org") ); KAboutData::setApplicationData(about); QUrl urlToShare; bool open; { QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("url"), i18n("URL to share")); parser.addOption(QCommandLineOption(QStringLiteral("open"), QStringLiteral("Open the file on the remote device"))); parser.addHelpOption(); about.setupCommandLine(&parser); parser.process(app); about.processCommandLine(&parser); if (parser.positionalArguments().count() != 1) { parser.showHelp(1); return 1; } urlToShare = QUrl::fromUserInput(parser.positionalArguments().constFirst(), QDir::currentPath()); open = parser.isSet(QStringLiteral("open")); } DevicesModel model; model.setDisplayFilter(DevicesModel::Paired | DevicesModel::Reachable); ShareDevicesProxyModel proxyModel; proxyModel.setSourceModel(&model); QDialog dialog; Ui::Dialog uidialog; uidialog.setupUi(&dialog); uidialog.devicePicker->setModel(&proxyModel); QString displayUrl; if (urlToShare.scheme() == QLatin1String("tel")) { displayUrl = urlToShare.toDisplayString(QUrl::RemoveScheme); uidialog.label->setText(i18n("Device to call %1 with:", displayUrl)); } else if (urlToShare.isLocalFile() && open) { displayUrl = urlToShare.toDisplayString(QUrl::PreferLocalFile); uidialog.label->setText(i18n("Device to send %1 to:", displayUrl)); } else { displayUrl = urlToShare.toDisplayString(QUrl::PreferLocalFile); uidialog.label->setText(i18n("Device to open %1 on:", displayUrl)); } dialog.setWindowTitle(displayUrl); if (dialog.exec() == QDialog::Accepted) { QUrl url = urlToShare; const int currentDeviceIndex = uidialog.devicePicker->currentIndex(); if(!url.isEmpty() && currentDeviceIndex >= 0) { const QString device = proxyModel.index(currentDeviceIndex, 0).data(DevicesModel::IdModelRole).toString(); const QString action = open && url.isLocalFile() ? QStringLiteral("openFile") : QStringLiteral("shareUrl"); - QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), "/modules/kdeconnect/devices/"+device+"/share", QStringLiteral("org.kde.kdeconnect.device.share"), action); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect/devices/") + device + QStringLiteral("/share"), QStringLiteral("org.kde.kdeconnect.device.share"), action); msg.setArguments({ url.toString() }); blockOnReply(DbusHelper::sessionBus().asyncCall(msg)); return 0; } else { QTextStream(stderr) << (i18n("Couldn't share %1", url.toString())) << endl; return 1; } } else { return 1; } } #include "kdeconnect-handler.moc"