File indexing completed on 2024-04-14 15:32:03
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Alejandro Fiestas Fiestas <afiestas@kde.org> 0003 * SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "receivefilejob.h" 0009 #include "bluedevil_kded.h" 0010 #include "filereceiversettings.h" 0011 #include "obexagent.h" 0012 0013 #include <QDir> 0014 #include <QIcon> 0015 #include <QTemporaryFile> 0016 #include <QTimer> 0017 0018 #include <KIO/CopyJob> 0019 #include <KJobTrackerInterface> 0020 #include <KLocalizedString> 0021 #include <KNotification> 0022 0023 #include <BluezQt/Adapter> 0024 #include <BluezQt/Device> 0025 #include <BluezQt/Manager> 0026 #include <BluezQt/ObexSession> 0027 0028 ReceiveFileJob::ReceiveFileJob(const BluezQt::Request<QString> &req, BluezQt::ObexTransferPtr transfer, BluezQt::ObexSessionPtr session, ObexAgent *parent) 0029 : KJob(parent) 0030 , m_speedBytes(0) 0031 , m_agent(parent) 0032 , m_transfer(transfer) 0033 , m_session(session) 0034 , m_request(req) 0035 { 0036 setCapabilities(Killable); 0037 // Pretend to be BlueDevil's file transfer agent even though we're run inside kded 0038 setProperty("desktopFileName", QStringLiteral("org.kde.bluedevilsendfile")); 0039 setProperty("immediateProgressReporting", true); 0040 } 0041 0042 QString ReceiveFileJob::deviceAddress() const 0043 { 0044 return m_deviceAddress; 0045 } 0046 0047 void ReceiveFileJob::start() 0048 { 0049 QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); 0050 } 0051 0052 bool ReceiveFileJob::doKill() 0053 { 0054 qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob-Kill"; 0055 m_transfer->cancel(); 0056 return true; 0057 } 0058 0059 void ReceiveFileJob::init() 0060 { 0061 qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob:"; 0062 qCDebug(BLUEDEVIL_KDED_LOG) << "\tName:" << m_transfer->name(); 0063 qCDebug(BLUEDEVIL_KDED_LOG) << "\tFilename:" << m_transfer->fileName(); 0064 qCDebug(BLUEDEVIL_KDED_LOG) << "\tStatus:" << m_transfer->status(); 0065 qCDebug(BLUEDEVIL_KDED_LOG) << "\tType:" << m_transfer->type(); 0066 qCDebug(BLUEDEVIL_KDED_LOG) << "\tSize:" << m_transfer->size(); 0067 qCDebug(BLUEDEVIL_KDED_LOG) << "\tTransferred:" << m_transfer->transferred(); 0068 0069 qCDebug(BLUEDEVIL_KDED_LOG) << "ObexSession:"; 0070 qCDebug(BLUEDEVIL_KDED_LOG) << "\tSource:" << m_session->source(); 0071 qCDebug(BLUEDEVIL_KDED_LOG) << "\tDestination:" << m_session->destination(); 0072 0073 connect(m_transfer.data(), &BluezQt::ObexTransfer::statusChanged, this, &ReceiveFileJob::statusChanged); 0074 connect(m_transfer.data(), &BluezQt::ObexTransfer::transferredChanged, this, &ReceiveFileJob::transferredChanged); 0075 0076 m_deviceName = m_session->destination(); 0077 0078 BluezQt::AdapterPtr adapter = m_agent->manager()->adapterForAddress(m_session->source()); 0079 if (!adapter) { 0080 qCDebug(BLUEDEVIL_KDED_LOG) << "No adapter for" << m_session->source(); 0081 showNotification(); 0082 return; 0083 } 0084 0085 BluezQt::DevicePtr device = adapter->deviceForAddress(m_session->destination()); 0086 if (!device) { 0087 qCDebug(BLUEDEVIL_KDED_LOG) << "No device for" << m_session->destination(); 0088 showNotification(); 0089 return; 0090 } 0091 0092 m_deviceName = device->name(); 0093 m_deviceAddress = device->address(); 0094 0095 if (m_agent->shouldAutoAcceptTransfer(m_deviceAddress)) { 0096 slotAccept(); 0097 return; 0098 } 0099 0100 FileReceiverSettings::self()->load(); 0101 switch (FileReceiverSettings::self()->autoAccept()) { 0102 case 0: // Never auto-accept transfers 0103 showNotification(); 0104 break; 0105 0106 case 1: // Auto-accept only from trusted devices 0107 if (device->isTrusted()) { 0108 qCDebug(BLUEDEVIL_KDED_LOG) << "Auto-accepting transfer for trusted device"; 0109 slotAccept(); 0110 } else { 0111 showNotification(); 0112 } 0113 break; 0114 0115 case 2: // Auto-accept all transfers 0116 qCDebug(BLUEDEVIL_KDED_LOG) << "Auto-accepting transfers for all devices"; 0117 slotAccept(); 0118 break; 0119 0120 default: // Unknown 0121 showNotification(); 0122 break; 0123 } 0124 } 0125 0126 void ReceiveFileJob::showNotification() 0127 { 0128 KNotification *notification = new KNotification(QStringLiteral("IncomingFile"), KNotification::Persistent, this); 0129 0130 notification->setTitle(QStringLiteral("%1 (%2)").arg(m_deviceName.toHtmlEscaped(), m_deviceAddress)); 0131 notification->setText(i18nc("Show a notification asking to authorize or deny an incoming file transfer to this computer from a Bluetooth device.", 0132 "%1 is sending you the file %2", 0133 m_deviceName.toHtmlEscaped(), 0134 m_transfer->name())); 0135 0136 QStringList actions; 0137 actions.append(i18nc("Button to accept the incoming file transfer and download it in the default download directory", "Accept")); 0138 actions.append(i18nc("Deny the incoming file transfer", "Cancel")); 0139 0140 notification->setActions(actions); 0141 0142 connect(notification, &KNotification::action1Activated, this, &ReceiveFileJob::slotAccept); 0143 connect(notification, &KNotification::action2Activated, this, &ReceiveFileJob::slotCancel); 0144 connect(notification, &KNotification::closed, this, &ReceiveFileJob::slotCancel); 0145 0146 notification->setComponentName(QStringLiteral("bluedevil")); 0147 0148 notification->sendEvent(); 0149 } 0150 0151 void ReceiveFileJob::slotAccept() 0152 { 0153 qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob-Accept"; 0154 0155 KIO::getJobTracker()->registerJob(this); 0156 0157 FileReceiverSettings::self()->load(); 0158 m_targetPath = FileReceiverSettings::self()->saveUrl().adjusted(QUrl::StripTrailingSlash); 0159 m_targetPath.setPath(m_targetPath.path() + QLatin1Char('/') + m_transfer->name()); 0160 0161 setTotalAmount(Files, 1); 0162 0163 Q_EMIT description(this, 0164 i18nc("@title job", "Receiving file"), 0165 QPair<QString, QString>(i18nc("File transfer origin", "From"), m_deviceName), 0166 QPair<QString, QString>(i18nc("File transfer destination", "To"), m_targetPath.toDisplayString())); 0167 0168 m_tempPath = createTempPath(m_transfer->name()); 0169 qCDebug(BLUEDEVIL_KDED_LOG) << "TempPath" << m_tempPath; 0170 0171 m_accepted = true; 0172 m_request.accept(m_tempPath); 0173 } 0174 0175 void ReceiveFileJob::slotCancel() 0176 { 0177 if (!m_accepted && m_transfer->status() == BluezQt::ObexTransfer::Queued) { 0178 qCDebug(BLUEDEVIL_KDED_LOG) << "Cancel Push"; 0179 m_request.reject(); 0180 setError(KJob::UserDefinedError); 0181 emitResult(); 0182 } 0183 } 0184 0185 void ReceiveFileJob::moveFinished(KJob *job) 0186 { 0187 if (job->error()) { 0188 qCDebug(BLUEDEVIL_KDED_LOG) << job->error(); 0189 qCDebug(BLUEDEVIL_KDED_LOG) << job->errorText(); 0190 setError(job->error()); 0191 setErrorText(i18n("Saving file failed")); 0192 0193 QFile::remove(m_tempPath); 0194 } 0195 0196 setProcessedAmount(Files, 1); 0197 0198 emitResult(); 0199 } 0200 0201 void ReceiveFileJob::statusChanged(BluezQt::ObexTransfer::Status status) 0202 { 0203 switch (status) { 0204 case BluezQt::ObexTransfer::Active: 0205 qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob-Transfer Active"; 0206 setTotalAmount(Bytes, m_transfer->size()); 0207 setProcessedAmount(Bytes, 0); 0208 m_time = QTime::currentTime(); 0209 break; 0210 0211 case BluezQt::ObexTransfer::Complete: { 0212 qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob-Transfer Complete"; 0213 KIO::CopyJob *job = KIO::move(QUrl::fromLocalFile(m_tempPath), m_targetPath, KIO::HideProgressInfo); 0214 job->setUiDelegate(nullptr); 0215 connect(job, &KIO::CopyJob::finished, this, &ReceiveFileJob::moveFinished); 0216 break; 0217 } 0218 0219 case BluezQt::ObexTransfer::Error: 0220 qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob-Transfer Error"; 0221 setError(KJob::UserDefinedError); 0222 setErrorText(i18n("Bluetooth transfer failed")); 0223 0224 // Delay emitResult to make sure notification is displayed even 0225 // when transfer errors right after accepting it 0226 QTimer::singleShot(500, this, [this]() { 0227 emitResult(); 0228 }); 0229 break; 0230 0231 default: 0232 qCDebug(BLUEDEVIL_KDED_LOG) << "Not implemented status: " << status; 0233 break; 0234 } 0235 } 0236 0237 void ReceiveFileJob::transferredChanged(quint64 transferred) 0238 { 0239 // qCDebug(BLUEDEVIL_KDED_LOG) << "ReceiveFileJob-Transferred" << transferred; 0240 0241 // If at least 1 second has passed since last update 0242 int secondsSinceLastTime = m_time.secsTo(QTime::currentTime()); 0243 if (secondsSinceLastTime > 0) { 0244 unsigned long speed = (transferred - m_speedBytes) / secondsSinceLastTime; 0245 emitSpeed(speed); 0246 0247 m_time = QTime::currentTime(); 0248 m_speedBytes = transferred; 0249 } 0250 0251 setProcessedAmount(Bytes, transferred); 0252 } 0253 0254 QString ReceiveFileJob::createTempPath(const QString &fileName) const 0255 { 0256 QString xdgCacheHome = QFile::decodeName(qgetenv("XDG_CACHE_HOME")); 0257 if (xdgCacheHome.isEmpty()) { 0258 xdgCacheHome = QDir::homePath() + QStringLiteral("/.cache"); 0259 } 0260 0261 xdgCacheHome.append(QLatin1String("/obexd/")); 0262 QString path = xdgCacheHome + fileName; 0263 0264 int i = 0; 0265 while (QFile::exists(path)) { 0266 path = xdgCacheHome + fileName + QString::number(i); 0267 i++; 0268 } 0269 0270 return path; 0271 }