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