service/DavNetworkLimits.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QNetworkReply> | ||
| 4 | #include <QNetworkRequest> | ||
| 5 | #include <QTimer> | ||
| 6 | #include <QUrl> | ||
| 7 | |||
| 8 | namespace DavNetworkLimits { | ||
| 9 | |||
| 10 | inline constexpr qint64 MaxResponseBytes = 10 * 1024 * 1024; | ||
| 11 | inline constexpr int RequestTimeoutMs = 30000; | ||
| 12 | inline constexpr const char *FailureProperty = "mailjdDavNetworkFailure"; | ||
| 13 | |||
| 14 | 12 | inline QString responseTooLargeMessage(qint64 bytes) { | |
| 15 | 24 | return QStringLiteral("DAV response too large (%1 bytes, limit %2 bytes)") | |
| 16 |
1/1✓ Branch 6 → 7 taken 12 times.
|
36 | .arg(bytes) |
| 17 |
1/1✓ Branch 8 → 9 taken 12 times.
|
24 | .arg(MaxResponseBytes); |
| 18 | } | ||
| 19 | |||
| 20 | 11 | inline void failReply(QNetworkReply *reply, const QString &error) { | |
| 21 |
6/12✓ Branch 2 → 3 taken 11 times.
✗ Branch 2 → 6 not taken.
✓ Branch 3 → 4 taken 11 times.
✓ Branch 4 → 5 taken 11 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 11 times.
✓ Branch 8 → 9 taken 11 times.
✗ Branch 8 → 10 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 11 times.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
|
11 | if (!reply || reply->property(FailureProperty).isValid()) |
| 22 | ✗ | return; | |
| 23 |
1/1✓ Branch 13 → 14 taken 11 times.
|
11 | reply->setProperty(FailureProperty, error); |
| 24 | 11 | reply->abort(); | |
| 25 | } | ||
| 26 | |||
| 27 | 2485 | inline void checkResponseSize(QNetworkReply *reply) { | |
| 28 |
8/12✓ Branch 2 → 3 taken 2485 times.
✗ Branch 2 → 6 not taken.
✓ Branch 3 → 4 taken 2485 times.
✓ Branch 4 → 5 taken 2485 times.
✓ Branch 5 → 6 taken 33 times.
✓ Branch 5 → 7 taken 2452 times.
✓ Branch 8 → 9 taken 2485 times.
✗ Branch 8 → 10 not taken.
✓ Branch 10 → 11 taken 33 times.
✓ Branch 10 → 12 taken 2452 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
|
2485 | if (!reply || reply->property(FailureProperty).isValid()) |
| 29 | 44 | return; | |
| 30 | |||
| 31 | const QVariant contentLength = | ||
| 32 |
1/1✓ Branch 12 → 13 taken 2452 times.
|
2452 | reply->header(QNetworkRequest::ContentLengthHeader); |
| 33 |
3/3✓ Branch 13 → 14 taken 2452 times.
✓ Branch 14 → 15 taken 43 times.
✓ Branch 14 → 23 taken 2409 times.
|
2452 | if (contentLength.isValid()) { |
| 34 | 43 | bool ok = false; | |
| 35 |
1/1✓ Branch 15 → 16 taken 43 times.
|
43 | const qint64 declaredSize = contentLength.toLongLong(&ok); |
| 36 |
3/4✓ Branch 16 → 17 taken 43 times.
✗ Branch 16 → 22 not taken.
✓ Branch 17 → 18 taken 11 times.
✓ Branch 17 → 22 taken 32 times.
|
43 | if (ok && declaredSize > MaxResponseBytes) { |
| 37 |
2/2✓ Branch 18 → 19 taken 11 times.
✓ Branch 19 → 20 taken 11 times.
|
11 | failReply(reply, responseTooLargeMessage(declaredSize)); |
| 38 | 11 | return; | |
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 |
1/1✓ Branch 23 → 24 taken 2441 times.
|
2441 | const qint64 buffered = reply->bytesAvailable(); |
| 43 |
1/2✗ Branch 24 → 25 not taken.
✓ Branch 24 → 29 taken 2441 times.
|
2441 | if (buffered > MaxResponseBytes) |
| 44 | ✗ | failReply(reply, responseTooLargeMessage(buffered)); | |
| 45 | 2452 | } | |
| 46 | |||
| 47 | 1224 | inline void apply(QNetworkReply *reply) { | |
| 48 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1224 times.
|
1224 | if (!reply) |
| 49 | ✗ | return; | |
| 50 | |||
| 51 |
2/5✓ Branch 5 → 6 taken 1224 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1224 times.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
|
1224 | auto *timer = new QTimer(reply); |
| 52 | 1224 | timer->setSingleShot(true); | |
| 53 |
1/1✓ Branch 9 → 10 taken 1224 times.
|
1224 | QObject::connect(timer, &QTimer::timeout, reply, [reply]() { |
| 54 | ✗ | failReply(reply, QStringLiteral("DAV request timed out")); | |
| 55 | ✗ | }); | |
| 56 |
1/1✓ Branch 11 → 12 taken 1224 times.
|
1224 | QObject::connect(reply, &QNetworkReply::finished, timer, &QTimer::stop); |
| 57 | 1224 | timer->start(RequestTimeoutMs); | |
| 58 | |||
| 59 | 1224 | QObject::connect(reply, &QNetworkReply::metaDataChanged, reply, | |
| 60 |
1/1✓ Branch 14 → 15 taken 1224 times.
|
2424 | [reply]() { checkResponseSize(reply); }); |
| 61 | 1224 | QObject::connect(reply, &QNetworkReply::readyRead, reply, | |
| 62 |
1/1✓ Branch 16 → 17 taken 1224 times.
|
1303 | [reply]() { checkResponseSize(reply); }); |
| 63 |
1/1✓ Branch 18 → 19 taken 1224 times.
|
2430 | QTimer::singleShot(0, reply, [reply]() { checkResponseSize(reply); }); |
| 64 | } | ||
| 65 | |||
| 66 | 1256 | inline QString failureReason(QNetworkReply *reply) { | |
| 67 |
4/8✓ Branch 2 → 3 taken 1256 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 1256 times.
✓ Branch 4 → 6 taken 1256 times.
✓ Branch 7 → 8 taken 1256 times.
✗ Branch 7 → 9 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 13 not taken.
|
2512 | return reply ? reply->property(FailureProperty).toString() : QString(); |
| 68 | } | ||
| 69 | |||
| 70 | // The DAV clients follow redirects only within the same origin. Qt compares | ||
| 71 | // the port literally, so a configured "https://host:443" would reject the | ||
| 72 | // server's own redirect to "https://host/…" ("Insecure redirect", F93-17). | ||
| 73 | // Requests therefore use the URL without its scheme's default port; the | ||
| 74 | // stored account URL (part of the keyring identity) is left untouched. | ||
| 75 | 741 | inline QString requestServerUrl(const QString &serverUrl) { | |
| 76 |
1/1✓ Branch 2 → 3 taken 741 times.
|
741 | QUrl url(serverUrl); |
| 77 |
10/12✓ Branch 4 → 5 taken 741 times.
✓ Branch 6 → 7 taken 669 times.
✓ Branch 6 → 9 taken 72 times.
✓ Branch 7 → 8 taken 669 times.
✓ Branch 8 → 9 taken 633 times.
✓ Branch 8 → 15 taken 36 times.
✓ Branch 12 → 13 taken 72 times.
✓ Branch 12 → 16 taken 633 times.
✓ Branch 21 → 22 taken 37 times.
✓ Branch 21 → 23 taken 704 times.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
|
2259 | if ((url.scheme() == QLatin1String("https") && url.port() == 443) || |
| 78 |
7/10✓ Branch 10 → 11 taken 705 times.
✓ Branch 13 → 14 taken 72 times.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 71 times.
✓ Branch 17 → 18 taken 705 times.
✓ Branch 17 → 19 taken 36 times.
✓ Branch 19 → 20 taken 741 times.
✗ Branch 19 → 21 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
|
1518 | (url.scheme() == QLatin1String("http") && url.port() == 80)) |
| 79 |
1/1✓ Branch 22 → 23 taken 37 times.
|
37 | url.setPort(-1); |
| 80 |
1/1✓ Branch 24 → 25 taken 741 times.
|
1482 | return url.toString(); |
| 81 | 741 | } | |
| 82 | |||
| 83 | 5394 | inline int effectivePort(const QUrl &url) { | |
| 84 | 5394 | const int port = url.port(); | |
| 85 |
2/2✓ Branch 3 → 4 taken 1006 times.
✓ Branch 3 → 5 taken 4388 times.
|
5394 | if (port >= 0) return port; |
| 86 |
3/3✓ Branch 5 → 6 taken 4388 times.
✓ Branch 10 → 11 taken 4370 times.
✓ Branch 10 → 12 taken 18 times.
|
8776 | return url.scheme().compare(QStringLiteral("https"), Qt::CaseInsensitive) == 0 ? 443 : 80; |
| 87 | } | ||
| 88 | |||
| 89 | // Resolves `href` against the DAV server URL and accepts it only on the | ||
| 90 | // same origin (scheme, host, effective port) without user info; "" when | ||
| 91 | // rejected. Shared by the CalDAV and DAV-Push clients. | ||
| 92 | 2713 | inline QString resolveSameOrigin(const QString &serverUrl, const QString &href) { | |
| 93 |
1/1✓ Branch 2 → 3 taken 2713 times.
|
2713 | const QUrl server(serverUrl); |
| 94 | 2713 | QUrl base = server; | |
| 95 |
7/7✓ Branch 5 → 6 taken 2713 times.
✓ Branch 8 → 9 taken 2713 times.
✓ Branch 10 → 11 taken 2711 times.
✓ Branch 10 → 20 taken 2 times.
✓ Branch 14 → 15 taken 2711 times.
✓ Branch 15 → 16 taken 2711 times.
✓ Branch 16 → 17 taken 2711 times.
|
5424 | if (!base.path().endsWith(QLatin1Char('/'))) base.setPath(base.path() + QLatin1Char('/')); |
| 96 |
1/1✓ Branch 20 → 21 taken 2713 times.
|
2713 | const QUrl candidate(href); |
| 97 |
4/4✓ Branch 21 → 22 taken 2713 times.
✓ Branch 22 → 23 taken 1816 times.
✓ Branch 22 → 24 taken 897 times.
✓ Branch 23 → 25 taken 1816 times.
|
2713 | const QUrl resolved = candidate.isRelative() ? base.resolved(candidate) : candidate; |
| 98 |
10/14✓ Branch 27 → 28 taken 2712 times.
✓ Branch 28 → 29 taken 2712 times.
✓ Branch 30 → 31 taken 2710 times.
✓ Branch 30 → 48 taken 2 times.
✓ Branch 36 → 37 taken 2690 times.
✓ Branch 36 → 48 taken 20 times.
✓ Branch 60 → 61 taken 2712 times.
✓ Branch 60 → 62 taken 1 time.
✓ Branch 62 → 63 taken 27 times.
✓ Branch 62 → 64 taken 2686 times.
✗ Branch 108 → 109 not taken.
✗ Branch 108 → 110 not taken.
✗ Branch 112 → 113 not taken.
✗ Branch 112 → 114 not taken.
|
10845 | if (!resolved.isValid() || resolved.scheme().compare(server.scheme(), Qt::CaseInsensitive) != 0 || |
| 99 |
6/10✓ Branch 32 → 33 taken 2710 times.
✓ Branch 34 → 35 taken 2710 times.
✓ Branch 56 → 57 taken 2710 times.
✓ Branch 56 → 58 taken 3 times.
✓ Branch 58 → 59 taken 2712 times.
✓ Branch 58 → 60 taken 1 time.
✗ Branch 98 → 99 not taken.
✗ Branch 98 → 100 not taken.
✗ Branch 103 → 104 not taken.
✗ Branch 103 → 105 not taken.
|
8133 | resolved.host().compare(server.host(), Qt::CaseInsensitive) != 0 || |
| 100 |
12/14✓ Branch 25 → 26 taken 2713 times.
✓ Branch 26 → 27 taken 2712 times.
✓ Branch 26 → 48 taken 1 time.
✓ Branch 37 → 38 taken 2690 times.
✓ Branch 38 → 39 taken 2690 times.
✓ Branch 39 → 40 taken 2689 times.
✓ Branch 39 → 48 taken 1 time.
✓ Branch 41 → 42 taken 2689 times.
✓ Branch 43 → 44 taken 2687 times.
✓ Branch 43 → 48 taken 2 times.
✓ Branch 54 → 55 taken 2710 times.
✓ Branch 54 → 56 taken 3 times.
✗ Branch 93 → 94 not taken.
✗ Branch 93 → 95 not taken.
|
13514 | effectivePort(resolved) != effectivePort(server) || !resolved.userName().isEmpty() || |
| 101 |
7/9✓ Branch 45 → 46 taken 2687 times.
✓ Branch 47 → 48 taken 1 time.
✓ Branch 47 → 49 taken 2686 times.
✓ Branch 50 → 51 taken 2687 times.
✓ Branch 50 → 52 taken 26 times.
✓ Branch 52 → 53 taken 2689 times.
✓ Branch 52 → 54 taken 24 times.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
|
5400 | !resolved.password().isEmpty()) |
| 102 | 27 | return {}; | |
| 103 |
1/1✓ Branch 65 → 66 taken 2686 times.
|
2686 | return resolved.toString(); |
| 104 | 2713 | } | |
| 105 | |||
| 106 | } // namespace DavNetworkLimits | ||
| 107 |