From 111ee7f0544c2c5ece7d074cd89cf300c8ac421a Mon Sep 17 00:00:00 2001 From: Albert Vaca Date: Tue, 10 Sep 2013 19:01:46 +0200 Subject: [PATCH] First iteration of filetransfer plugin Now it just downloads to the user desktop payloads received in packages with type kdeconnect.filetransfer. Added the package type define. Created an an auxiliar QFile-like class with a small needed modification. Added a commented test in the filetransfer plugin code (to be moved). --- kded/backends/loopback/loopbackdevicelink.cpp | 3 +- kded/networkpackagetypes.h | 1 + kded/plugins/filetransfer/CMakeLists.txt | 4 + .../plugins/filetransfer/autoclosingqfile.cpp | 27 ++++++ kded/plugins/filetransfer/autoclosingqfile.h | 43 +++++++++ kded/plugins/filetransfer/filetransferjob.cpp | 96 +++++++++++++++++++ kded/plugins/filetransfer/filetransferjob.h | 55 +++++++++++ .../filetransfer/filetransferplugin.cpp | 61 +++++++++++- .../plugins/filetransfer/filetransferplugin.h | 7 +- .../kdeconnect_filetransfer.desktop | 2 +- 10 files changed, 290 insertions(+), 9 deletions(-) create mode 100644 kded/plugins/filetransfer/autoclosingqfile.cpp create mode 100644 kded/plugins/filetransfer/autoclosingqfile.h create mode 100644 kded/plugins/filetransfer/filetransferjob.cpp create mode 100644 kded/plugins/filetransfer/filetransferjob.h diff --git a/kded/backends/loopback/loopbackdevicelink.cpp b/kded/backends/loopback/loopbackdevicelink.cpp index defbbefa2..79638712e 100644 --- a/kded/backends/loopback/loopbackdevicelink.cpp +++ b/kded/backends/loopback/loopbackdevicelink.cpp @@ -30,13 +30,12 @@ LoopbackDeviceLink::LoopbackDeviceLink(const QString& deviceId, LoopbackLinkProv bool LoopbackDeviceLink::sendPackage(const NetworkPackage& input) { - NetworkPackage output(""); + NetworkPackage output(QString::null); NetworkPackage::unserialize(input.serialize(), &output); //LoopbackDeviceLink does not need deviceTransferInfo if (input.hasPayload()) { QIODevice* device = input.payload(); - device->open(QIODevice::ReadOnly); output.setPayload(device); } diff --git a/kded/networkpackagetypes.h b/kded/networkpackagetypes.h index 1fde97eca..f60b8fa69 100644 --- a/kded/networkpackagetypes.h +++ b/kded/networkpackagetypes.h @@ -30,6 +30,7 @@ #define PACKAGE_TYPE_TELEPHONY QString("kdeconnect.telephony") #define PACKAGE_TYPE_CLIPBOARD QString("kdeconnect.clipboard") #define PACKAGE_TYPE_MPRIS QString("kdeconnect.mpris") +#define PACKAGE_TYPE_FILETRANSFER QString("kdeconnect.filetransfer") #endif // NETWORKPACKAGETYPES_H diff --git a/kded/plugins/filetransfer/CMakeLists.txt b/kded/plugins/filetransfer/CMakeLists.txt index 139760bc4..f75b4a1fd 100644 --- a/kded/plugins/filetransfer/CMakeLists.txt +++ b/kded/plugins/filetransfer/CMakeLists.txt @@ -4,6 +4,7 @@ find_package(QCA2 REQUIRED) include_directories( ${QJSON_INCLUDE_DIR} + ${KIO_INCLUDE_DIR} ${QCA2_INCLUDE_DIR} ) @@ -12,6 +13,8 @@ include_directories(${KDE4_INCLUDES}) set(kdeconnect_filetransfer_SRCS filetransferplugin.cpp + filetransferjob.cpp + autoclosingqfile.cpp ../kdeconnectplugin.cpp ../pluginloader.cpp ../../networkpackage.cpp @@ -23,6 +26,7 @@ kde4_add_plugin(kdeconnect_filetransfer ${kdeconnect_filetransfer_SRCS}) target_link_libraries(kdeconnect_filetransfer ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} + ${KDE4_KIO_LIBS} ${QT_QTNETWORK_LIBRARY} ${QJSON_LIBRARIES} ${QCA2_LIBRARIES} diff --git a/kded/plugins/filetransfer/autoclosingqfile.cpp b/kded/plugins/filetransfer/autoclosingqfile.cpp new file mode 100644 index 000000000..ef2aeea3d --- /dev/null +++ b/kded/plugins/filetransfer/autoclosingqfile.cpp @@ -0,0 +1,27 @@ +/* + * 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 "autoclosingqfile.h" + +AutoClosingQFile::AutoClosingQFile(const QString& name) + : QFile(name) +{ + +} diff --git a/kded/plugins/filetransfer/autoclosingqfile.h b/kded/plugins/filetransfer/autoclosingqfile.h new file mode 100644 index 000000000..ab25be8e1 --- /dev/null +++ b/kded/plugins/filetransfer/autoclosingqfile.h @@ -0,0 +1,43 @@ +/* + * 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 AUTOCLOSINGQFILE_H +#define AUTOCLOSINGQFILE_H + +#include +#include + +class AutoClosingQFile : public QFile +{ + Q_OBJECT +public: + + AutoClosingQFile(const QString &name); + virtual qint64 readData(char* data, qint64 maxlen) { + qint64 read = QFile::readData(data, maxlen); + if (read == -1 || read == bytesAvailable()) { + close(); + } + return read; + } +}; + + +#endif // AUTOCLOSINGQFILE_H diff --git a/kded/plugins/filetransfer/filetransferjob.cpp b/kded/plugins/filetransfer/filetransferjob.cpp new file mode 100644 index 000000000..5535f104c --- /dev/null +++ b/kded/plugins/filetransfer/filetransferjob.cpp @@ -0,0 +1,96 @@ +/* + * 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 "filetransferjob.h" + +#include +#include + +FileTransferJob::FileTransferJob(QIODevice* origin, const KUrl& destination): KJob() +{ + mDestination = destination; + mOrigin = origin; +} + +void FileTransferJob::start() +{ + //Open destination file + + QTemporaryFile tmp; + tmp.setAutoRemove(false); + tmp.open(); + + mTempDestination = KIO::open(tmp.fileName(), QIODevice::WriteOnly); + connect(mTempDestination, SIGNAL(open(KIO::Job*)), this, SLOT(open(KIO::Job*))); + mTempDestination->start(); + +} + +void FileTransferJob::open(KIO::Job* job) +{ + Q_UNUSED(job); + + //Open source file + + mOrigin->open(QIODevice::ReadOnly); + + Q_ASSERT(mOrigin->isOpen()); + + connect(mOrigin, SIGNAL(readyRead()),this, SLOT(readyRead())); + connect(mOrigin, SIGNAL(aboutToClose()),this, SLOT(sourceFinished())); + if (mOrigin->bytesAvailable() > 0) readyRead(); + +} + +void FileTransferJob::readyRead() +{ + //Copy a chunk of data + + int bytes = qMin(qint64(4096), mOrigin->bytesAvailable()); + QByteArray data = mOrigin->read(bytes); + mTempDestination->write(data); + + if(!mOrigin->atEnd()) + QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection); +} + +void FileTransferJob::sourceFinished() +{ + qDebug() << "sourceFinished"; + + readyRead(); //Read the last chunk of data, if any + + qDebug() << "Finished" << mTempDestination->url(); + KIO::FileCopyJob* job = KIO::file_move(mTempDestination->url(), mDestination); + connect(job, SIGNAL(result(KJob*)), this, SLOT(moveResult(KJob*))); + job->start(); + + delete mOrigin; //TODO: Use shared pointers +} + +void FileTransferJob::moveResult(KJob* job) +{ + //TODO: Error handling, cleanup + qDebug() << "Move result"; + qDebug() << job->errorString(); + qDebug() << job->errorText(); + emitResult(); +} + diff --git a/kded/plugins/filetransfer/filetransferjob.h b/kded/plugins/filetransfer/filetransferjob.h new file mode 100644 index 000000000..5cf365f1c --- /dev/null +++ b/kded/plugins/filetransfer/filetransferjob.h @@ -0,0 +1,55 @@ +/* + * 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 FILETRANSFERJOB_H +#define FILETRANSFERJOB_H + +#include +#include + +#include +#include +#include +#include +#include + +class FileTransferJob : public KJob +{ + Q_OBJECT + +public: + FileTransferJob(QIODevice* origin, const KUrl& destination); + virtual void start(); + KUrl destination() { return mDestination; } + +public Q_SLOTS: + void readyRead(); + void moveResult(KJob*); + void open(KIO::Job*); + void sourceFinished(); + +private: + KIO::FileJob* mTempDestination; + KUrl mDestination; + QIODevice* mOrigin; + +}; + +#endif // FILETRANSFERJOB_H diff --git a/kded/plugins/filetransfer/filetransferplugin.cpp b/kded/plugins/filetransfer/filetransferplugin.cpp index 41cdf7d94..95c7ba097 100644 --- a/kded/plugins/filetransfer/filetransferplugin.cpp +++ b/kded/plugins/filetransfer/filetransferplugin.cpp @@ -20,21 +20,72 @@ #include "filetransferplugin.h" +#include +#include +#include + +#include +#include +#include + +#include "filetransferjob.h" +#include "autoclosingqfile.h" + K_PLUGIN_FACTORY( KdeConnectPluginFactory, registerPlugin< FileTransferPlugin >(); ) K_EXPORT_PLUGIN( KdeConnectPluginFactory("kdeconnect_filetransfer", "kdeconnect_filetransfer") ) - FileTransferPlugin::FileTransferPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { - - + //TODO: Use downloads user path + //TODO: Be able to change this from config + mDestinationDir = QDesktopServices::DesktopLocation; } bool FileTransferPlugin::receivePackage(const NetworkPackage& np) { - Q_UNUSED(np); - return false; + //TODO: Move this code to a test and do a diff between files + /* + if (np.type() == PACKAGE_TYPE_PING) { + + NetworkPackage np(PACKAGE_TYPE_FILETRANSFER); + np.set("filename", mDestinationDir + "/itworks.txt"); + //TODO: Use shared pointers + AutoClosingQFile* file = new AutoClosingQFile("/home/vaka/KdeConnect.apk"); //Test file to transfer + + np.setPayload(file); + + device()->sendPackage(np); + + return true; + } + */ + + if (np.type() != PACKAGE_TYPE_FILETRANSFER) return false; + + if (np.hasPayload()) { + QString filename = np.get("filename"); + QIODevice* incoming = np.payload(); + FileTransferJob* job = new FileTransferJob(incoming,filename); + connect(job,SIGNAL(result(KJob*)), this, SLOT(finished(KJob*))); + job->start(); + } + return true; + +} + +void FileTransferPlugin::finished(KJob* job) +{ + qDebug() << "File transfer finished"; + + FileTransferJob* transferJob = (FileTransferJob*)job; + KNotification* notification = new KNotification("pingReceived"); //KNotification::Persistent + notification->setPixmap(KIcon("dialog-ok").pixmap(48, 48)); + notification->setComponentData(KComponentData("kdeconnect", "kdeconnect")); + notification->setTitle(i18n("Transfer finished")); + notification->setText(transferJob->destination().fileName()); + //TODO: Add action "open destination folder" + notification->sendEvent(); } diff --git a/kded/plugins/filetransfer/filetransferplugin.h b/kded/plugins/filetransfer/filetransferplugin.h index 25fae9b5e..f9fa7864a 100644 --- a/kded/plugins/filetransfer/filetransferplugin.h +++ b/kded/plugins/filetransfer/filetransferplugin.h @@ -21,7 +21,8 @@ #ifndef FILETRANFERPLUGIN_H #define FILETRANFERPLUGIN_H -#include +#include +#include #include "../kdeconnectplugin.h" @@ -36,6 +37,10 @@ public: public Q_SLOTS: virtual bool receivePackage(const NetworkPackage& np); virtual void connected() { } + void finished(KJob*); + +private: + QString mDestinationDir; }; diff --git a/kded/plugins/filetransfer/kdeconnect_filetransfer.desktop b/kded/plugins/filetransfer/kdeconnect_filetransfer.desktop index 58fc4da2b..09bc1a9df 100644 --- a/kded/plugins/filetransfer/kdeconnect_filetransfer.desktop +++ b/kded/plugins/filetransfer/kdeconnect_filetransfer.desktop @@ -11,5 +11,5 @@ X-KDE-PluginInfo-Website=http://albertvaka.wordpress.com X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true Icon=folder-downloads -Name=(Stub) File transfer +Name=File transfer Comment=Send and receive files from dolphin