From 11996f56ca4f4bc214ebc4b895a519ff55573832 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Tue, 5 May 2015 00:41:39 +0200 Subject: [PATCH] Drop dependency of KIO from kdeconnectcore Use QNetworkAccessManager for storing the files we receive instead of directly KIO, then provide the KIO-based QNetworkAccessManager from the daemon. This way we'll still get the KIO network-transparency, but the library only depenends on QtNetwork, which will be easier to get in some platforms. After this change, we only depend on KConfig, KI18n and KCoreAddons, which are tier1 and really easy to work with on any platform. REVIEW: 123325 --- CMakeLists.txt | 2 +- core/CMakeLists.txt | 2 +- core/daemon.cpp | 10 ++++ core/daemon.h | 2 + core/filetransferjob.cpp | 112 +++++++++------------------------------ core/filetransferjob.h | 32 +++++++---- daemon/CMakeLists.txt | 4 +- daemon/kdeconnectd.cpp | 14 +++++ 8 files changed, 77 insertions(+), 101 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dbaa03953..05b00e4fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ find_package(ECM 0.0.9 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake) find_package(Qt5 5.2 REQUIRED COMPONENTS Quick Test) -find_package(KF5 REQUIRED COMPONENTS I18n KIO ConfigWidgets DBusAddons KCMUtils IconThemes) +find_package(KF5 REQUIRED COMPONENTS I18n ConfigWidgets DBusAddons IconThemes) find_package(Qca-qt5 2.1.0 REQUIRED) include_directories(${CMAKE_SOURCE_DIR}) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f4659592e..4d752512e 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -33,7 +33,7 @@ add_library(kdeconnectcore ${kdeconnectcore_SRCS}) target_link_libraries(kdeconnectcore PUBLIC Qt5::Network - KF5::KIOCore + KF5::CoreAddons qca-qt5 PRIVATE Qt5::DBus diff --git a/core/daemon.cpp b/core/daemon.cpp index a06358981..683427f85 100644 --- a/core/daemon.cpp +++ b/core/daemon.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "core_debug.h" #include "kdeconnectconfig.h" @@ -185,6 +186,15 @@ QString Daemon::announcedName() return KdeConnectConfig::instance()->name(); } +QNetworkAccessManager* Daemon::networkAccessManager() +{ + static QPointer manager; + if (!manager) { + manager = new QNetworkAccessManager(this); + } + return manager; +} + Daemon::~Daemon() { diff --git a/core/daemon.h b/core/daemon.h index a2e2cfc22..c073384a0 100644 --- a/core/daemon.h +++ b/core/daemon.h @@ -30,6 +30,7 @@ class NetworkPackage; class DeviceLink; class Device; +class QNetworkAccessManager; class KDECONNECTCORE_EXPORT Daemon : public QObject @@ -62,6 +63,7 @@ public Q_SLOTS: virtual void requestPairing(Device *d) = 0; virtual void reportError(const QString &title, const QString &description) = 0; + virtual QNetworkAccessManager* networkAccessManager(); Q_SIGNALS: Q_SCRIPTABLE void deviceAdded(const QString& id); diff --git a/core/filetransferjob.cpp b/core/filetransferjob.cpp index 27ed8e82b..f503f6a30 100644 --- a/core/filetransferjob.cpp +++ b/core/filetransferjob.cpp @@ -1,5 +1,6 @@ /* * Copyright 2013 Albert Vaca + * Copyright 2015 Aleix Pol i Gonzalez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,6 +20,7 @@ */ #include "filetransferjob.h" +#include "daemon.h" #include #include @@ -30,28 +32,25 @@ FileTransferJob::FileTransferJob(const QSharedPointer& origin, qint64 size, const QUrl& destination) : KJob() , mOrigin(origin) - , mDestinationJob(0) - , mDeviceName("KDE Connect") + , mReply(Q_NULLPTR) + , mDeviceName("KDE Connect") //TODO: Actually fetch the device name , mDestination(destination) , mSpeedBytes(0) - , mSize(size) , mWritten(0) { + Q_ASSERT(mOrigin); if (mDestination.scheme().isEmpty()) { qWarning() << "Destination QUrl" << mDestination << "lacks a scheme. Setting its scheme to 'file'."; mDestination.setScheme("file"); } - Q_ASSERT(destination.isLocalFile()); + if (size >= 0) { + setTotalAmount(Bytes, size); + } setCapabilities(Killable); qCDebug(KDECONNECT_CORE) << "FileTransferJob Downloading payload to" << destination; } -void FileTransferJob::openFinished(KJob* job) -{ - qCDebug(KDECONNECT_CORE) << job->errorString(); -} - void FileTransferJob::start() { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); @@ -62,10 +61,10 @@ void FileTransferJob::doStart() { description(this, i18n("Receiving file over KDE-Connect"), QPair(i18nc("File transfer origin", "From"), - QString(mDeviceName)) + mDeviceName) ); - QUrl destCheck = mDestination; - if (destCheck.isLocalFile() && QFile::exists(destCheck.toLocalFile())) { + + if (mDestination.isLocalFile() && QFile::exists(mDestination.toLocalFile())) { setError(2); setErrorText(i18n("Filename already present")); emitResult(); @@ -76,7 +75,6 @@ void FileTransferJob::doStart() void FileTransferJob::startTransfer() { - setTotalAmount(Bytes, mSize); setProcessedAmount(Bytes, 0); mTime = QTime::currentTime(); description(this, i18n("Receiving file over KDE-Connect"), @@ -84,90 +82,32 @@ void FileTransferJob::startTransfer() QString(mDeviceName)), QPair(i18nc("File transfer destination", "To"), mDestination.toLocalFile())); - mDestinationJob = KIO::open(mDestination, QIODevice::WriteOnly); - QFile(mDestination.toLocalFile()).open(QIODevice::WriteOnly | QIODevice::Truncate); //KIO won't create the file if it doesn't exist - connect(mDestinationJob, SIGNAL(open(KIO::Job*)), this, SLOT(open(KIO::Job*))); - connect(mDestinationJob, SIGNAL(result(KJob*)), this, SLOT(openFinished(KJob*))); - - //Open destination file - mDestinationJob->start(); + mReply = Daemon::instance()->networkAccessManager()->put(QNetworkRequest(mDestination), mOrigin.data()); + connect(mReply, &QNetworkReply::uploadProgress, this, [this](qint64 bytesSent, qint64 /*bytesTotal*/) { + setProcessedAmount(Bytes, bytesSent); + emitSpeed(bytesSent/mTime.elapsed()); + }); + connect(mReply, &QNetworkReply::finished, this, &FileTransferJob::transferFinished); } -void FileTransferJob::open(KIO::Job* job) +void FileTransferJob::transferFinished() { - Q_UNUSED(job); - - //qCDebug(KDECONNECT_CORE) << "FileTransferJob open"; - - if (!mOrigin) { - qCDebug(KDECONNECT_CORE) << "FileTransferJob: Origin is null"; - return; - } - - //Open source file - mOrigin->open(QIODevice::ReadOnly); - Q_ASSERT(mOrigin->isOpen()); - - connect(mOrigin.data(), SIGNAL(readyRead()),this, SLOT(readyRead())); - connect(mOrigin.data(), SIGNAL(aboutToClose()),this, SLOT(sourceFinished())); - if (mOrigin->bytesAvailable() > 0) readyRead(); - -} - -void FileTransferJob::readyRead() -{ - int bytes = qMin(qint64(4096), mOrigin->bytesAvailable()); - QByteArray data = mOrigin->read(bytes); - mDestinationJob->write(data); - mWritten += data.size(); - setProcessedAmount(Bytes, mWritten); - - //qCDebug(KDECONNECT_CORE) << "readyRead" << mSize << mWritten << bytes; - - if (mSize > -1) { - //If a least 1 second has passed since last update - int secondsSinceLastTime = mTime.secsTo(QTime::currentTime()); - if (secondsSinceLastTime > 0 && mSpeedBytes > 0) { - float speed = (mWritten - mSpeedBytes) / float(secondsSinceLastTime); - emitSpeed(speed); - - mTime = QTime::currentTime(); - mSpeedBytes = mWritten; - } else if(mSpeedBytes == 0) { - mSpeedBytes = mWritten; - } - } - - if (mSize > -1 && mWritten >= mSize) { //At the end or expected size reached - mOrigin->close(); - //sourceFinished(); - } else if (mOrigin->bytesAvailable() > 0) { - QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); - } -} - -void FileTransferJob::sourceFinished() -{ - //Make sure we do not enter this function again - disconnect(mOrigin.data(), SIGNAL(aboutToClose()),this, SLOT(sourceFinished())); - //TODO: MD5-check the file - if (mSize > -1 && mWritten != mSize) { - qCDebug(KDECONNECT_CORE) << "Received incomplete file (" << mWritten << " of " << mSize << " bytes)"; - setError(1); - setErrorText(i18n("Received incomplete file")); + if (mReply->error()) { + qCDebug(KDECONNECT_CORE) << "Couldn't transfer the file successfully" << mReply->errorString(); + setError(mReply->error()); + setErrorText(i18n("Received incomplete file: %1", mReply->errorString())); } else { - qCDebug(KDECONNECT_CORE) << "Finished transfer" << mDestinationJob->url(); + qCDebug(KDECONNECT_CORE) << "Finished transfer" << mDestination; } - mDestinationJob->close(); - mDestinationJob->deleteLater(); + emitResult(); } bool FileTransferJob::doKill() { - if (mDestinationJob) { - mDestinationJob->close(); + if (mReply) { + mReply->close(); } if (mOrigin) { mOrigin->close(); diff --git a/core/filetransferjob.h b/core/filetransferjob.h index 973bd3a87..859a30321 100644 --- a/core/filetransferjob.h +++ b/core/filetransferjob.h @@ -1,5 +1,6 @@ /* * Copyright 2013 Albert Vaca + * Copyright 2015 Aleix Pol i Gonzalez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,39 +29,48 @@ #include #include -#include -#include +#include +#include +#include +/** + * @short It will stream a device into a url destination + * + * Given a QIODevice, the file transfer job will use the system's QNetworkAccessManager + * for putting the stream into the requested location. + */ class FileTransferJob : public KJob { Q_OBJECT public: + /** + * @p origin specifies the data to read from. + * @p size specifies the expected size of the stream we're reading. + * @p destination specifies where these contents should be stored + */ FileTransferJob(const QSharedPointer& origin, qint64 size, const QUrl &destination); - virtual void start(); + virtual void start() Q_DECL_OVERRIDE; QUrl destination() const { return mDestination; } void setDeviceName(const QString &deviceName) { mDeviceName = deviceName; } -public Q_SLOTS: +private Q_SLOTS: void doStart(); - void readyRead(); - void open(KIO::Job*); - void sourceFinished(); - void openFinished(KJob*); protected: - virtual bool doKill(); + bool doKill() Q_DECL_OVERRIDE; private: void startTransfer(); + void transferFinished(); + QSharedPointer mOrigin; - KIO::FileJob* mDestinationJob; + QNetworkReply* mReply; QString mDeviceName; QUrl mDestination; QTime mTime; qulonglong mSpeedBytes; - qint64 mSize; qint64 mWritten; }; diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 852e7794b..0aec94248 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -1,11 +1,11 @@ project(kdeconnectd) -find_package(KF5 REQUIRED COMPONENTS Notifications) +find_package(KF5 REQUIRED COMPONENTS Notifications KIO) add_definitions(-DTRANSLATION_DOMAIN="kdeconnect-kded") add_executable(kdeconnectd kdeconnectd.cpp) -target_link_libraries(kdeconnectd kdeconnectcore KF5::DBusAddons KF5::Notifications KF5::I18n Qt5::Widgets) +target_link_libraries(kdeconnectd kdeconnectcore KF5::KIOWidgets KF5::DBusAddons KF5::Notifications KF5::I18n Qt5::Widgets) configure_file(kdeconnectd.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdeconnectd.desktop) configure_file(org.kde.kdeconnect.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kdeconnect.service) diff --git a/daemon/kdeconnectd.cpp b/daemon/kdeconnectd.cpp index c43bc9be1..522cf719a 100644 --- a/daemon/kdeconnectd.cpp +++ b/daemon/kdeconnectd.cpp @@ -25,10 +25,12 @@ #include #include +#include #include #include #include +#include #include "core/daemon.h" #include "core/device.h" @@ -68,6 +70,7 @@ class DesktopDaemon : public Daemon public: DesktopDaemon(QObject* parent = Q_NULLPTR) : Daemon(parent) + , m_nam(Q_NULLPTR) {} void requestPairing(Device* d) Q_DECL_OVERRIDE @@ -87,6 +90,17 @@ public: { KNotification::event(KNotification::Error, title, description); } + + QNetworkAccessManager* networkAccessManager() Q_DECL_OVERRIDE + { + if (!m_nam) { + m_nam = new KIO::AccessManager(this); + } + return m_nam; + } + +private: + QNetworkAccessManager* m_nam; }; int main(int argc, char* argv[])