service/DavPushXml.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QByteArray> | ||
| 4 | #include <QHash> | ||
| 5 | #include <QString> | ||
| 6 | #include <optional> | ||
| 7 | |||
| 8 | #include "data/DavPushModels.h" | ||
| 9 | |||
| 10 | // Sprint 96 S96.1.2 / S96.2.3: namespace-exact DAV-Push XML (WebDAV-Push | ||
| 11 | // namespace https://bitfire.at/webdav-push). Unlike the QDom helpers of the | ||
| 12 | // CalDAV client this parser honours namespaces: a foreign element with the | ||
| 13 | // same local name never supplies a topic, key or message. | ||
| 14 | // | ||
| 15 | // Profiles (ADR-96-2): the installed Nextcloud dav_push 1.0.2 returns no | ||
| 16 | // supported-triggers and no type attribute on the VAPID key; the draft | ||
| 17 | // requires both. Both are accepted, security-relevant values (key bytes, | ||
| 18 | // topic, structure) are validated strictly. | ||
| 19 | namespace DavPushXml { | ||
| 20 | |||
| 21 | inline const QString kNamespace = QStringLiteral("https://bitfire.at/webdav-push"); | ||
| 22 | |||
| 23 | // Bounds for documents from the network. | ||
| 24 | constexpr int kMaxDepth = 16; | ||
| 25 | constexpr int kMaxMessageBytes = 16 * 1024; | ||
| 26 | |||
| 27 | // The push properties a discovery PROPFIND asks for (prefix "P"). | ||
| 28 | QByteArray discoveryProperties(); | ||
| 29 | |||
| 30 | // Per collection href (as written in the response) the classified offer, | ||
| 31 | // read from successful (2xx) propstat blocks only. Collections whose push | ||
| 32 | // properties were all answered 404 are Unsupported. nullopt for broken XML. | ||
| 33 | std::optional<QHash<QString, DavPushOffer>> parseDiscovery(const QByteArray &multistatus, | ||
| 34 | QString *error = nullptr); | ||
| 35 | |||
| 36 | // The push-register body for a Web Push subscription, accepted by the | ||
| 37 | // 1.0.2 profile and the draft (content-encoding and the content-update | ||
| 38 | // trigger are ignored by 1.0.2). | ||
| 39 | QByteArray registerBody(const QString &pushResource, const QByteArray &publicKey, const QByteArray &authSecret); | ||
| 40 | |||
| 41 | struct PushMessage { | ||
| 42 | QString topic; // empty for a VAPID key rotation notice | ||
| 43 | bool contentUpdate = false; | ||
| 44 | QString syncToken; // opaque; "" when the message carries none | ||
| 45 | bool propertyUpdate = false; | ||
| 46 | bool transportsChanged = false; // property-update naming P:transports | ||
| 47 |
3/4✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 6 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 6 not taken.
|
8 | bool isKeyRotation() const { return topic.isEmpty() && transportsChanged; } |
| 48 | }; | ||
| 49 | |||
| 50 | // Parses a decrypted push message. Rejects DTDs, more than kMaxDepth | ||
| 51 | // levels, more than kMaxMessageBytes and any root other than P:push-message. | ||
| 52 | std::optional<PushMessage> parsePushMessage(const QByteArray &xml, QString *error = nullptr); | ||
| 53 | |||
| 54 | // HTTP Expires of a registration: IMF-fixdate ("… GMT", RFC 9110) and the | ||
| 55 | // dav_push 1.0.2 form "Sat, 10 Oct 2026 19:12:38 +0000+" (numeric offset | ||
| 56 | // and a trailing "+"). Epoch seconds, nullopt when unusable. | ||
| 57 | std::optional<qint64> parseHttpDate(const QString &value); | ||
| 58 | |||
| 59 | } // namespace DavPushXml | ||
| 60 |