service/DavPushClient.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QByteArray> | ||
| 4 | #include <QObject> | ||
| 5 | #include <QString> | ||
| 6 | #include <functional> | ||
| 7 | |||
| 8 | class QNetworkAccessManager; | ||
| 9 | class QNetworkReply; | ||
| 10 | |||
| 11 | // Sprint 96 S96.2.3: WebDAV-Push subscription requests of one DAV account — | ||
| 12 | // POST push-register (registration and renewal) and DELETE of the own | ||
| 13 | // registration URL. Uses the DAV credentials only towards the DAV origin; | ||
| 14 | // a Location on another origin is a protocol failure, never followed. | ||
| 15 | // | ||
| 16 | // Profiles (ADR-96-2): draft 201/204, dav_push 1.0.2 201 with an XML body; | ||
| 17 | // Expires as IMF-fixdate or "… +0000+". A missing or unusable Expires is a | ||
| 18 | // failure: an unlimited subscription is never assumed. | ||
| 19 | class DavPushClient final : public QObject { | ||
| 20 | Q_OBJECT | ||
| 21 | public: | ||
| 22 | enum class Outcome { | ||
| 23 | Ok, | ||
| 24 | NotFound, // register: collection gone; unregister: already removed (counts as done) | ||
| 25 | Unauthorized, // 401: DAV credentials rejected | ||
| 26 | Forbidden, // 403: privileges or no-trigger-supported precondition | ||
| 27 | Incompatible, // 400/415, missing/foreign Location, missing Expires | ||
| 28 | RateLimited, // 429/503 (retryAfterSeconds when given) | ||
| 29 | ServerError, // other 5xx | ||
| 30 | Transport, // network/TLS/timeout/limit | ||
| 31 | }; | ||
| 32 | 198 | Q_ENUM(Outcome) | |
| 33 | |||
| 34 | struct Result { | ||
| 35 | quint64 request = 0; | ||
| 36 | Outcome outcome = Outcome::Transport; | ||
| 37 | int status = 0; | ||
| 38 | QString collectionPath; // register only | ||
| 39 | QString location; // exact registration URL from the server | ||
| 40 | qint64 expiresAt = 0; // local epoch seconds (server Date skew applied) | ||
| 41 | int retryAfterSeconds = 0; | ||
| 42 | QString detail; // technical, redacted (no URLs, no secrets) | ||
| 43 | }; | ||
| 44 | |||
| 45 | DavPushClient(const QString &serverUrl, const QString &username, const QString &password, | ||
| 46 | QObject *parent = nullptr); | ||
| 47 | |||
| 48 | quint64 registerSubscription(const QString &collectionPath, const QString &pushResource, | ||
| 49 | const QByteArray &publicKey, const QByteArray &authSecret); | ||
| 50 | quint64 unregisterSubscription(const QString ®istrationUrl); | ||
| 51 | |||
| 52 | // Test seams. | ||
| 53 | void setNetworkAccessManager(QNetworkAccessManager *nam); | ||
| 54 | 7 | void setClock(std::function<qint64()> clock) { m_clock = std::move(clock); } | |
| 55 | |||
| 56 | signals: | ||
| 57 | void registered(const DavPushClient::Result &result); | ||
| 58 | void unregistered(const DavPushClient::Result &result); | ||
| 59 | |||
| 60 | private: | ||
| 61 | QByteArray authHeader() const; | ||
| 62 | Result classify(QNetworkReply *reply, quint64 request) const; | ||
| 63 | void finishRegister(QNetworkReply *reply, quint64 request, const QString &collectionPath); | ||
| 64 | qint64 now() const; | ||
| 65 | |||
| 66 | QNetworkAccessManager *m_nam = nullptr; | ||
| 67 | QString m_serverUrl; | ||
| 68 | QString m_username; | ||
| 69 | QString m_password; | ||
| 70 | quint64 m_nextRequest = 0; | ||
| 71 | std::function<qint64()> m_clock; | ||
| 72 | }; | ||
| 73 | |||
| 74 |
4/7✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 14 not taken.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
✗ Branch 14 → 15 not taken.
|
2 | Q_DECLARE_METATYPE(DavPushClient::Result) |
| 75 |