[network/kdeconnect-kde] /: Keep files that arrive whole but not in the announced size
Albert Vaca Cintora <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 3435f69097534b4c9f077988ec2986e849213be1 by Albert Vaca Cintora, on behalf of Méven Car.
Committed on 28/07/2026 at 05:24.
Pushed by albertvaka into branch 'master'.
Keep files that arrive whole but not in the announced size
The check added in 7cb4de20c compared the bytes written against the size the
sender announced and took every difference for a failed transfer, deleting
the file. Two of those differences are not failures.
A sender can announce a size it then does not keep to. The Android app asks
the content provider how big the file is and opens the stream in a separate
call, so a provider whose stream does not match the size in its own listing
sends more than was announced. The file arrives whole and is deleted for it.
A sender that does not know the size announces -1. The Android app does that
whenever the content provider reports no size, which happens for a file that
is not stored on the phone. Nothing was announced, so the comparison could
never hold and every such file was deleted. Three lines above, startTransfer
already tells this case apart with m_size >= 0.
Only a file that stops short of an announced size is an incomplete one. Keep
the rest, and note in the log when more arrived than was announced.
BUG: 523547
M +10 -3 core/filetransferjob.cpp
M +89 -0 tests/sendfiletest.cpp
https://invent.kde.org/network/kdeconnect-kde/-/commit/3435f69097534b4c9f077988ec2986e849213be1
diff --git a/core/filetransferjob.cpp b/core/filetransferjob.cpp
index 611fb0d74..39716d234 100644
--- a/core/filetransferjob.cpp
+++ b/core/filetransferjob.cpp
@@ -105,15 +105,22 @@ void FileTransferJob::transferFinished()
setErrorText(m_reply->errorString());
} else {
// TODO: MD5-check the file
- if (m_size == m_written) {
- qCDebug(KDECONNECT_CORE) << "Finished transfer" << m_destination;
- } else {
+ // A payload size of -1 means the sender does not know how much it is going to send,
+ // and a sender can also announce less than it ends up sending, so only a file that
+ // stops short of the announced size is an incomplete one.
+ if (m_size >= 0 && m_written < m_size) {
qCDebug(KDECONNECT_CORE) << "Received incomplete file (" << m_written << "/" << m_size << "bytes ), deleting";
deleteDestinationFile();
setError(3);
setErrorText(i18n("Received incomplete file from: %1", m_from));
+ } else {
+ if (m_size >= 0 && m_written > m_size) {
+ qCInfo(KDECONNECT_CORE) << "Received" << (m_written - m_size) << "bytes more than the announced" << m_size
+ << "and keeping them, the sender announced a size it did not keep to";
+ }
+ qCDebug(KDECONNECT_CORE) << "Finished transfer" << m_destination;
}
}
emitResult();
diff --git a/tests/sendfiletest.cpp b/tests/sendfiletest.cpp
index 50112476d..fef954ba3 100644
--- a/tests/sendfiletest.cpp
+++ b/tests/sendfiletest.cpp
@@ -130,6 +130,95 @@ private Q_SLOTS:
QCOMPARE(resultFile.readAll(), originFile.readAll());
}
+ void testMoreDataThanAnnounced()
+ {
+ // A sender can announce a size that turns out to be smaller than what it then sends,
+ // for instance when the file it reads is being rewritten as it goes. The file arrives
+ // whole, so it has to be kept.
+ const QString aFile = QFINDTESTDATA("sendfiletest.cpp");
+ const QString destFile = QDir::tempPath() + QStringLiteral("/kdeconnect-test-longer-than-announced");
+ QFile(destFile).remove();
+
+ DeviceInfo deviceInfo = KdeConnectConfig::instance().deviceInfo();
+ KdeConnectConfig::instance().addTrustedDevice(deviceInfo);
+
+ Device *device = new Device(this, deviceInfo.id);
+ m_daemon->addDevice(device);
+
+ QSharedPointer<QFile> f(new QFile(aFile));
+ const qint64 actualSize = f->size();
+ QVERIFY(actualSize > 16);
+
+ NetworkPacket np(PACKET_TYPE_SHARE_REQUEST);
+ np.setPayload(f, actualSize - 16);
+
+ CompositeUploadJob *job = new CompositeUploadJob(device, false);
+ UploadJob *uj = new UploadJob(np);
+ job->addSubjob(uj);
+ job->start();
+
+ f->open(QIODevice::ReadWrite);
+
+ FileTransferJob *ft = np.createPayloadTransferJob(QUrl::fromLocalFile(destFile));
+ QSignalSpy spyTransfer(ft, &KJob::result);
+ ft->start();
+
+ QVERIFY(spyTransfer.count() || spyTransfer.wait());
+
+ if (ft->error()) {
+ qWarning() << "fterror" << ft->errorString();
+ }
+ QCOMPARE(ft->error(), 0);
+
+ QFile resultFile(destFile);
+ QVERIFY(resultFile.exists());
+ QCOMPARE(resultFile.size(), actualSize);
+ }
+
+ void testUnannouncedSize()
+ {
+ // A sender that does not know how big the payload is announces a size of -1, which the
+ // Android app does whenever the content provider has no size to give. Nothing was
+ // promised, so nothing can fall short of it.
+ const QString aFile = QFINDTESTDATA("sendfiletest.cpp");
+ const QString destFile = QDir::tempPath() + QStringLiteral("/kdeconnect-test-unannounced-size");
+ QFile(destFile).remove();
+
+ DeviceInfo deviceInfo = KdeConnectConfig::instance().deviceInfo();
+ KdeConnectConfig::instance().addTrustedDevice(deviceInfo);
+
+ Device *device = new Device(this, deviceInfo.id);
+ m_daemon->addDevice(device);
+
+ QSharedPointer<QFile> f(new QFile(aFile));
+ const qint64 actualSize = f->size();
+
+ NetworkPacket np(PACKET_TYPE_SHARE_REQUEST);
+ np.setPayload(f, -1);
+
+ CompositeUploadJob *job = new CompositeUploadJob(device, false);
+ UploadJob *uj = new UploadJob(np);
+ job->addSubjob(uj);
+ job->start();
+
+ f->open(QIODevice::ReadWrite);
+
+ FileTransferJob *ft = np.createPayloadTransferJob(QUrl::fromLocalFile(destFile));
+ QSignalSpy spyTransfer(ft, &KJob::result);
+ ft->start();
+
+ QVERIFY(spyTransfer.count() || spyTransfer.wait());
+
+ if (ft->error()) {
+ qWarning() << "fterror" << ft->errorString();
+ }
+ QCOMPARE(ft->error(), 0);
+
+ QFile resultFile(destFile);
+ QVERIFY(resultFile.exists());
+ QCOMPARE(resultFile.size(), actualSize);
+ }
+
void testTimeout()
{
const QString aFile = QFINDTESTDATA("sendfiletest.cpp");