Report error Virus Detected. Updated tests.

Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
alex-z 2023-11-30 15:00:18 +01:00 committed by allexzander
parent 66b0383fee
commit c30e526bc8
8 changed files with 96 additions and 1 deletions

View File

@ -366,6 +366,9 @@ void BulkPropagatorJob::slotPutFinishedOneFile(const BulkUploadItem &singleFile,
singleFile._item->_requestId = job->requestId();
if (singleFile._item->_httpErrorCode != 200) {
commonErrorHandling(singleFile._item, fileReply[QStringLiteral("message")].toString());
const auto exceptionParsed = getExceptionFromReply(job->reply());
singleFile._item->_errorExceptionName = exceptionParsed.first;
singleFile._item->_errorExceptionMessage = exceptionParsed.second;
return;
}

View File

@ -358,7 +358,15 @@ void PropagateItemJob::reportClientStatuses()
propagator()->account()->reportClientStatus(ClientStatusReporting::Status::DownloadError_ConflictInvalidCharacters);
} else if (_item->_httpErrorCode != 0 && _item->_httpErrorCode != 200 && _item->_httpErrorCode != 201 && _item->_httpErrorCode != 204) {
if (_item->_direction == SyncFileItem::Up) {
propagator()->account()->reportClientStatus(ClientStatusReporting::Status::UploadError_ServerError);
const auto isCodeBadReqOrUnsupportedMediaType = (_item->_httpErrorCode == 400 || _item->_httpErrorCode == 415);
const auto isExceptionInfoPresent = !_item->_errorExceptionName.isEmpty() && !_item->_errorExceptionMessage.isEmpty();
if (isCodeBadReqOrUnsupportedMediaType && isExceptionInfoPresent
&& _item->_errorExceptionName.contains(QStringLiteral("UnsupportedMediaType"))
&& _item->_errorExceptionMessage.contains(QStringLiteral("virus"), Qt::CaseInsensitive)) {
propagator()->account()->reportClientStatus(ClientStatusReporting::Status::UploadError_Virus_Detected);
} else {
propagator()->account()->reportClientStatus(ClientStatusReporting::Status::UploadError_ServerError);
}
} else {
propagator()->account()->reportClientStatus(ClientStatusReporting::Status::DownloadError_ServerError);
}

View File

@ -48,6 +48,7 @@ inline bool fileIsStillChanging(const OCC::SyncFileItem &item)
namespace OCC {
inline QByteArray getEtagFromReply(QNetworkReply *reply)
{
QByteArray ocEtag = parseEtag(reply->rawHeader("OC-ETag"));
@ -62,6 +63,52 @@ inline QByteArray getEtagFromReply(QNetworkReply *reply)
return ret;
}
inline QPair<QByteArray, QByteArray> getExceptionFromReply(QNetworkReply *reply)
{
Q_ASSERT(reply);
if (!reply) {
qDebug() << "Could not parse null reply";
return {};
}
const auto httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// only for BadRequest and UnsupportedMediaType
if (httpStatusCode != 400 && httpStatusCode != 415) {
return {};
}
// we must not modify the buffer
const auto replyBody = reply->peek(reply->bytesAvailable());
// parse exception name
auto exceptionNameStartIndex = replyBody.indexOf(QByteArrayLiteral("<s:exception>"));
if (exceptionNameStartIndex == -1) {
qDebug() << "Could not parse exception. No <s:exception> tag.";
return {};
}
exceptionNameStartIndex += QByteArrayLiteral("<s:exception>").size();
const auto exceptionNameEndIndex = replyBody.indexOf('<', exceptionNameStartIndex);
if (exceptionNameEndIndex == -1) {
return {};
}
const auto exceptionName = replyBody.mid(exceptionNameStartIndex, exceptionNameEndIndex - exceptionNameStartIndex);
// parse exception message
auto exceptionMessageStartIndex = replyBody.indexOf(QByteArrayLiteral("<s:message>"), exceptionNameEndIndex);
if (exceptionMessageStartIndex == -1) {
qDebug() << "Could not parse exception. No <s:message> tag.";
return {exceptionName, {}};
}
exceptionMessageStartIndex += QByteArrayLiteral("<s:message>").size();
const auto exceptionMessageEndIndex = replyBody.indexOf('<', exceptionMessageStartIndex);
if (exceptionMessageEndIndex == -1) {
return {exceptionName, {}};
}
const auto exceptionMessage = replyBody.mid(exceptionMessageStartIndex, exceptionMessageEndIndex - exceptionMessageStartIndex);
return {exceptionName, exceptionMessage};
}
/**
* Given an error from the network, map to a SyncFileItem::Status error
*/

View File

@ -112,6 +112,9 @@ bool PollJob::finished()
_item->_requestId = requestId();
_item->_status = classifyError(err, _item->_httpErrorCode);
_item->_errorString = errorString();
const auto exceptionParsed = getExceptionFromReply(reply());
_item->_errorExceptionName = exceptionParsed.first;
_item->_errorExceptionMessage = exceptionParsed.second;
if (_item->_status == SyncFileItem::FatalError || _item->_httpErrorCode >= 400) {
if (_item->_status != SyncFileItem::FatalError

View File

@ -411,6 +411,9 @@ void PropagateUploadFileNG::slotPutFinished()
_item->_httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
_item->_requestId = job->requestId();
commonErrorHandling(job);
const auto exceptionParsed = getExceptionFromReply(job->reply());
_item->_errorExceptionName = exceptionParsed.first;
_item->_errorExceptionMessage = exceptionParsed.second;
return;
}
@ -496,6 +499,9 @@ void PropagateUploadFileNG::slotMoveJobFinished()
if (err != QNetworkReply::NoError) {
commonErrorHandling(job);
const auto exceptionParsed = getExceptionFromReply(job->reply());
_item->_errorExceptionName = exceptionParsed.first;
_item->_errorExceptionMessage = exceptionParsed.second;
return;
}

View File

@ -218,6 +218,9 @@ void PropagateUploadFileV1::slotPutFinished()
QNetworkReply::NetworkError err = job->reply()->error();
if (err != QNetworkReply::NoError) {
commonErrorHandling(job);
const auto exceptionParsed = getExceptionFromReply(job->reply());
_item->_errorExceptionName = exceptionParsed.first;
_item->_errorExceptionMessage = exceptionParsed.second;
return;
}

View File

@ -287,6 +287,8 @@ public:
quint16 _httpErrorCode = 0;
RemotePermissions _remotePerm;
QString _errorString; // Contains a string only in case of error
QString _errorExceptionName; // Contains a server exception string only in case of error
QString _errorExceptionMessage; // Contains a server exception message string only in case of error
QByteArray _responseTimeStamp;
QByteArray _requestId; // X-Request-Id of the failed request
quint32 _affectedItems = 1; // the number of affected items by the operation on this item.

View File

@ -9,6 +9,7 @@
#include "propagatedownload.h"
#include "owncloudpropagator_p.h"
#include "syncenginetestutils.h"
using namespace OCC;
namespace OCC {
@ -76,6 +77,28 @@ private slots:
QCOMPARE(parseEtag(test.first), QByteArray(test.second));
}
}
void testParseException()
{
QNetworkRequest request;
request.setUrl(QStringLiteral("http://cloud.example.de/"));
const auto body = QByteArrayLiteral(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<d:error xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\">\n"
"<s:exception>Sabre\\Exception\\UnsupportedMediaType</s:exception>\n"
"<s:message>Virus detected!</s:message>\n"
"</d:error>");
const auto reply = new FakeErrorReply(QNetworkAccessManager::PutOperation,
request,
this,
415, body);
const auto exceptionParsed = OCC::getExceptionFromReply(reply);
// verify parsing succeeded
QVERIFY(!exceptionParsed.first.isEmpty());
QVERIFY(!exceptionParsed.second.isEmpty());
// verify buffer is not changed
QCOMPARE(reply->readAll().size(), body.size());
}
};
QTEST_APPLESS_MAIN(TestNextcloudPropagator)