fix(quota): parse quota values from servers as double

The server can respond with values like `2.58440798353E+12` instead of
a plain number like `2584407983530`.
`QVariant::toLongLong` does not recognise that as a valid number and
returns `0` instead, breaking the sync for some.

Also added a fallback value in case parsing the value as double doesn't
work either.

Fixes #8555

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
This commit is contained in:
Jyrki Gadinger 2025-10-10 10:11:42 +02:00
parent 0d83bb2bf9
commit 01526c34e5
6 changed files with 59 additions and 10 deletions

View File

@ -14,7 +14,7 @@ namespace OCC {
/**
* Represent the quota for each folder retrieved from the server
* bytesUsed: space used in bytes
* bytesAvailale: free space available in bytes or
* bytesAvailable: free space available in bytes or
* -1: Uncomputed free space - new folder (externally created) not yet scanned by the server
* -2: Unknown free space
* -3: Unlimited free space.

View File

@ -512,8 +512,14 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(const QString &fi
// all folders will contain both
if (map.contains(FolderQuota::usedBytesC) && map.contains(FolderQuota::availableBytesC)) {
_folderQuota = {map.value(FolderQuota::usedBytesC).toLongLong(),
map.value(FolderQuota::availableBytesC).toLongLong()};
// The server can respond with e.g. "2.58440798353E+12" for the quota
// therefore: parse the string as a double and cast it to i64
auto ok = false;
auto quotaValue = static_cast<int64_t>(map.value(FolderQuota::usedBytesC).toDouble(&ok));
_folderQuota.bytesUsed = ok ? quotaValue : -1;
quotaValue = static_cast<int64_t>(map.value(FolderQuota::availableBytesC).toDouble(&ok));
_folderQuota.bytesAvailable = ok ? quotaValue : -1;
qCWarning(lcDiscovery) << "Setting quota for" << file
<< "bytesUsed:" << _folderQuota.bytesUsed
<< "bytesAvailable:" << _folderQuota.bytesAvailable;

View File

@ -476,12 +476,14 @@ void LsColJob::propertyMapToRemoteInfo(const QMap<QString, QString> &map, Remote
result.sizeOfFolder = map.value("size").toInt();
}
if (result.isDirectory && map.contains(FolderQuota::usedBytesC)) {
result.folderQuota.bytesUsed = map.value(FolderQuota::usedBytesC).toLongLong();
}
if (result.isDirectory && map.contains(FolderQuota::availableBytesC)) {
result.folderQuota.bytesAvailable = map.value(FolderQuota::availableBytesC).toLongLong();
if (result.isDirectory && map.contains(FolderQuota::usedBytesC) && map.contains(FolderQuota::availableBytesC)) {
// The server can respond with e.g. "2.58440798353E+12" for the quota
// therefore: parse the string as a double and cast it to i64
auto ok = false;
auto quotaValue = static_cast<int64_t>(map.value(FolderQuota::usedBytesC).toDouble(&ok));
result.folderQuota.bytesUsed = ok ? quotaValue : -1;
quotaValue = static_cast<int64_t>(map.value(FolderQuota::availableBytesC).toDouble(&ok));
result.folderQuota.bytesAvailable = ok ? quotaValue : -1;
}
}

View File

@ -417,7 +417,7 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
xml.writeTextElement(davUri, QStringLiteral("getlastmodified"), stringDate);
xml.writeTextElement(davUri, QStringLiteral("getcontentlength"), QString::number(fileInfo.size));
xml.writeTextElement(davUri, QStringLiteral("getetag"), QStringLiteral("\"%1\"").arg(QString::fromLatin1(fileInfo.etag)));
xml.writeTextElement(ocUri, QStringLiteral("quota-available-bytes"), std::to_string(fileInfo.folderQuota.bytesAvailable));
xml.writeTextElement(ocUri, QStringLiteral("quota-available-bytes"), fileInfo.folderQuota.bytesAvailableString());
xml.writeTextElement(ocUri, QStringLiteral("quota-used-bytes"), std::to_string(fileInfo.folderQuota.bytesUsed));
xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("GSRDNVCKW") : QStringLiteral("GRDNVCKW"));
if (fileInfo.isShared) {

View File

@ -134,6 +134,25 @@ public:
struct FolderQuota {
int64_t bytesUsed = 0;
int64_t bytesAvailable = 5000000000;
FolderQuota() : bytesUsed{0}, bytesAvailable{5000000000} {};
FolderQuota(int64_t bytesUsed, int64_t bytesAvailable)
: bytesUsed{bytesUsed}
, bytesAvailable{bytesAvailable}
{}
QString bytesAvailableString() const {
if (_bytesAvailableString.isEmpty()) {
return QString::number(bytesAvailable);
}
return _bytesAvailableString;
}
void setBytesAvailableString(const QString &bytesAvailableString) { _bytesAvailableString = bytesAvailableString; }
private:
QString _bytesAvailableString = QStringLiteral("");
};
void addChild(const FileInfo &info);

View File

@ -199,6 +199,28 @@ private slots:
QVERIFY(completeSpy.findItem("nofileid")->_errorString.contains("file id"));
QVERIFY(completeSpy.findItem("nopermissions/A")->_errorString.contains("permission"));
}
void testQuotaReportedAsDouble()
{
FakeFolder fakeFolder{ FileInfo() };
fakeFolder.remoteModifier().mkdir("doubleValue");
fakeFolder.remoteModifier().find("doubleValue")->folderQuota.setBytesAvailableString("2.345E+12");
fakeFolder.remoteModifier().mkdir("intValue");
fakeFolder.remoteModifier().find("intValue")->folderQuota.setBytesAvailableString("2345000000000");
fakeFolder.remoteModifier().mkdir("unlimited");
fakeFolder.remoteModifier().find("unlimited")->folderQuota.setBytesAvailableString("-3");
fakeFolder.remoteModifier().mkdir("invalidValue");
fakeFolder.remoteModifier().find("invalidValue")->folderQuota.setBytesAvailableString("maybe like, 3 GB");
ItemCompletedSpy completeSpy(fakeFolder);
QVERIFY(fakeFolder.syncOnce());
int64_t expectedValue = 2345000000000;
QCOMPARE(completeSpy.findItem("doubleValue")->_folderQuota.bytesAvailable, expectedValue);
QCOMPARE(completeSpy.findItem("intValue")->_folderQuota.bytesAvailable, expectedValue);
QCOMPARE(completeSpy.findItem("unlimited")->_folderQuota.bytesAvailable, -3);
QCOMPARE(completeSpy.findItem("invalidValue")->_folderQuota.bytesAvailable, -1);
}
};
QTEST_GUILESS_MAIN(TestRemoteDiscovery)