File indexing completed on 2024-04-21 04:56:45

0001 /*
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  * SPDX-FileCopyrightText: 2015 Aleix Pol i Gonzalez <aleixpol@kde.org>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #include "filetransferjob.h"
0009 #include "daemon.h"
0010 #include <core_debug.h>
0011 
0012 #include <QDebug>
0013 #include <QFileInfo>
0014 #include <QNetworkAccessManager>
0015 #include <qalgorithms.h>
0016 
0017 #include <KFileUtils>
0018 #include <KLocalizedString>
0019 
0020 FileTransferJob::FileTransferJob(const NetworkPacket *np, const QUrl &destination)
0021     : KJob()
0022     , m_origin(np->payload())
0023     , m_reply(nullptr)
0024     , m_from(QStringLiteral("KDE Connect"))
0025     , m_destination(destination)
0026     , m_written(0)
0027     , m_size(np->payloadSize())
0028     , m_np(np)
0029     , m_autoRename(false)
0030 {
0031     Q_ASSERT(m_origin);
0032     // Disabled this assert: QBluetoothSocket doesn't report "->isReadable() == true" until it's connected
0033     // Q_ASSERT(m_origin->isReadable());
0034     if (m_destination.scheme().isEmpty()) {
0035         qCWarning(KDECONNECT_CORE) << "Destination QUrl" << m_destination << "lacks a scheme. Setting its scheme to 'file'.";
0036         m_destination.setScheme(QStringLiteral("file"));
0037     }
0038 
0039     setCapabilities(Killable);
0040     qCDebug(KDECONNECT_CORE) << "FileTransferJob Downloading payload to" << destination << "size:" << m_size;
0041 }
0042 
0043 void FileTransferJob::start()
0044 {
0045     QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
0046     // qCDebug(KDECONNECT_CORE) << "FileTransferJob start";
0047 }
0048 
0049 void FileTransferJob::doStart()
0050 {
0051     if (m_destination.isLocalFile() && QFile::exists(m_destination.toLocalFile())) {
0052         if (m_autoRename) {
0053             QFileInfo fileInfo(m_destination.toLocalFile());
0054             QString path = fileInfo.path();
0055             QString fileName = fileInfo.fileName();
0056             m_destination.setPath(path + QStringLiteral("/") + KFileUtils::suggestName(QUrl(path), fileName), QUrl::DecodedMode);
0057         } else {
0058             setError(2);
0059             setErrorText(i18n("Filename already present"));
0060             emitResult();
0061             return;
0062         }
0063     }
0064 
0065     if (m_origin->bytesAvailable())
0066         startTransfer();
0067     connect(m_origin.data(), &QIODevice::readyRead, this, &FileTransferJob::startTransfer);
0068 }
0069 
0070 void FileTransferJob::startTransfer()
0071 {
0072     // Don't put each ready read
0073     if (m_reply)
0074         return;
0075 
0076     setProcessedAmount(Bytes, 0);
0077 
0078     QNetworkRequest req(m_destination);
0079     if (m_size >= 0) {
0080         setTotalAmount(Bytes, m_size);
0081         req.setHeader(QNetworkRequest::ContentLengthHeader, m_size);
0082     }
0083     m_reply = Daemon::instance()->networkAccessManager()->put(req, m_origin.data());
0084 
0085     connect(m_reply, &QNetworkReply::uploadProgress, this, [this](qint64 bytesSent, qint64 /*bytesTotal*/) {
0086         if (!m_timer.isValid())
0087             m_timer.start();
0088         setProcessedAmount(Bytes, bytesSent);
0089 
0090         const auto elapsed = m_timer.elapsed();
0091         if (elapsed > 0) {
0092             emitSpeed((1000 * bytesSent) / elapsed);
0093         }
0094 
0095         m_written = bytesSent;
0096     });
0097     connect(m_reply, &QNetworkReply::errorOccurred, this, &FileTransferJob::transferFailed);
0098     connect(m_reply, &QNetworkReply::finished, this, &FileTransferJob::transferFinished);
0099 }
0100 
0101 void FileTransferJob::transferFailed(QNetworkReply::NetworkError error)
0102 {
0103     qCDebug(KDECONNECT_CORE) << "Couldn't transfer the file successfully" << error << m_reply->errorString();
0104     setError(error);
0105     setErrorText(i18n("Received incomplete file: %1", m_reply->errorString()));
0106     emitResult();
0107 
0108     m_reply->close();
0109 }
0110 
0111 void FileTransferJob::transferFinished()
0112 {
0113     // TODO: MD5-check the file
0114     if (m_size == m_written) {
0115         qCDebug(KDECONNECT_CORE) << "Finished transfer" << m_destination;
0116         emitResult();
0117     } else {
0118         qCDebug(KDECONNECT_CORE) << "Received incomplete file (" << m_written << "/" << m_size << "bytes ), deleting";
0119 
0120         deleteDestinationFile();
0121 
0122         setError(3);
0123         setErrorText(i18n("Received incomplete file from: %1", m_from));
0124         emitResult();
0125     }
0126 }
0127 
0128 void FileTransferJob::deleteDestinationFile()
0129 {
0130     if (m_destination.isLocalFile() && QFile::exists(m_destination.toLocalFile())) {
0131         QFile::remove(m_destination.toLocalFile());
0132     }
0133 }
0134 
0135 bool FileTransferJob::doKill()
0136 {
0137     if (m_reply) {
0138         m_reply->close();
0139     }
0140     if (m_origin) {
0141         m_origin->close();
0142     }
0143 
0144     deleteDestinationFile();
0145 
0146     return true;
0147 }
0148 
0149 #include "moc_filetransferjob.cpp"