MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - service - CalDavClient.h (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 100.0 % 1 1
Test Date: 2026-07-27 17:53:44 Functions: 100.0 % 1 1
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include <QList>
       4              : #include <QObject>
       5              : #include <QSet>
       6              : #include <QString>
       7              : 
       8              : #include "data/CalendarModels.h"
       9              : 
      10              : class QNetworkAccessManager;
      11              : class QNetworkReply;
      12              : class QTimer;
      13              : 
      14              : // ═══════════════════════════════════════════════════════
      15              : // CalDAV client for calendar discovery and sync (Sprint 32 – T-331)
      16              : // Architecture mirrors CardDavClient (Sprint 16).
      17              : // ═══════════════════════════════════════════════════════
      18              : 
      19              : class CalDavClient : public QObject {
      20              :   Q_OBJECT
      21              : public:
      22              :   CalDavClient(const QString &serverUrl, const QString &username,
      23              :                const QString &password, QObject *parent = nullptr);
      24              : 
      25              :   /// Discover available calendars via PROPFIND
      26              :   void discoverCalendars();
      27              : 
      28              :   /// Sync all events (VEVENT) from a calendar via REPORT
      29              :   void syncCalendar(const QString &calendarPath);
      30              : 
      31              :   /// Sync all tasks (VTODO) from a calendar via REPORT
      32              :   void syncTasks(const QString &calendarPath);
      33              : 
      34              :   // ── Sprint 39 – T-530: Write API ──────────────────────
      35              : 
      36              :   /// Create a new event on the server (HTTP PUT + If-None-Match)
      37              :   void createEvent(const QString &calendarPath, const CalendarEvent &event);
      38              :   /// Update an existing event (HTTP PUT + If-Match: etag)
      39              :   void updateEvent(const CalendarEvent &event);
      40              :   /// Delete an event (HTTP DELETE + If-Match: etag)
      41              :   void deleteEvent(const CalendarEvent &event);
      42              : 
      43              :   /// Create a new task on the server
      44              :   void createTask(const QString &calendarPath, const CalendarTask &task);
      45              :   /// Update an existing task
      46              :   void updateTask(const CalendarTask &task);
      47              :   /// Delete a task
      48              :   void deleteTask(const CalendarTask &task);
      49              : 
      50              :   /// Serialize a CalendarEvent to iCalendar format (RFC 5545 VEVENT)
      51              :   static QByteArray eventToICalendar(const CalendarEvent &event);
      52              :   /// Serialize a CalendarTask to iCalendar format (RFC 5545 VTODO)
      53              :   static QByteArray taskToICalendar(const CalendarTask &task);
      54              : 
      55              :   /// Test seam: replace the network access manager (takes ownership). Lets
      56              :   /// unit tests inject a fake NAM that returns canned replies for the write API.
      57              :   void setNetworkAccessManager(QNetworkAccessManager *nam);
      58              : 
      59              :   // T-79.F1/H8: number of network replies still in flight. The client
      60              :   // emits allRequestsFinished() when it drops to 0 — callers connect that
      61              :   // to deleteLater() instead of a fixed-lifetime timer.
      62            2 :   int pendingReplies() const { return m_pendingReplies; }
      63              : 
      64              : signals:
      65              :   // T-79.F1/H8: the last outstanding reply finished (or the inactivity
      66              :   // watchdog gave up). Safe point to deleteLater() the client.
      67              :   void allRequestsFinished();
      68              : 
      69              :   void calendarsDiscovered(const QList<CalendarInfo> &calendars);
      70              :   void eventsSynced(const QString &calendarPath,
      71              :                     const QList<CalendarEvent> &events);
      72              :   void tasksSynced(const QString &calendarPath,
      73              :                    const QList<CalendarTask> &tasks);
      74              :   void syncFailed(const QString &error);
      75              : 
      76              :   // T-530 write result signals
      77              :   void eventSaved(const CalendarEvent &event);
      78              :   void eventDeleted(const QString &uid);
      79              :   void taskSaved(const CalendarTask &task);
      80              :   void taskDeleted(const QString &uid);
      81              :   void writeFailed(const QString &error);
      82              : 
      83              : private:
      84              :   friend class TestCalDav;
      85              :   friend class TestSprint39;
      86              :   friend class TestSprint79ICal;
      87              :   friend class TestSprint79Services;
      88              : 
      89              :   // T-79.F1/H8: register a dispatched reply for lifetime tracking.
      90              :   // Call AFTER connecting the reply's business handler, so follow-up
      91              :   // requests started from result signals are counted before the zero
      92              :   // check runs.
      93              :   void trackReply(QNetworkReply *reply);
      94              :   void onInactivityTimeout();
      95              : 
      96              :   void onDiscoverReply(QNetworkReply *reply);
      97              :   void onSyncEventsReply(QNetworkReply *reply, const QString &calendarPath);
      98              :   void onSyncTasksReply(QNetworkReply *reply, const QString &calendarPath);
      99              : 
     100              :   /// Build the CalDAV resource URL for a new resource path + UID
     101              :   QString resourceUrl(const QString &calendarPath, const QString &uid) const;
     102              :   QString resourceUrlForExistingResource(const QString &resourceHref,
     103              :                                          const QString &calendarPath,
     104              :                                          const QString &uid) const;
     105              :   QString resolveDavUrl(const QString &href) const;
     106              : 
     107              :   /// Parse iCal text into CalendarEvent list
     108              :   static QList<CalendarEvent> parseICalEvents(const QByteArray &xmlData);
     109              :   static bool parseICalEvents(const QByteArray &xmlData,
     110              :                               QList<CalendarEvent> *events,
     111              :                               QString *error);
     112              : 
     113              :   /// Parse iCal text into CalendarTask list
     114              :   static QList<CalendarTask> parseICalTasks(const QByteArray &xmlData);
     115              :   static bool parseICalTasks(const QByteArray &xmlData,
     116              :                              QList<CalendarTask> *tasks,
     117              :                              QString *error);
     118              : 
     119              :   QByteArray authHeader() const;
     120              : 
     121              :   // Validate the complete RFC entity-tag grammar before setting If-Match.
     122              :   static bool isValidEtag(const QString &etag);
     123              : 
     124              :   QNetworkAccessManager *m_nam = nullptr;
     125              :   QString m_serverUrl;
     126              :   QString m_username;
     127              :   QString m_password;
     128              : 
     129              :   // T-79.F1/H8: outstanding-reply tracking + inactivity watchdog.
     130              :   // The watchdog restarts on every request/response; a stall longer than
     131              :   // kInactivityTimeoutMs fails the outstanding work (syncFailed +
     132              :   // writeFailed) and releases the client via allRequestsFinished().
     133              :   int m_pendingReplies = 0;
     134              :   QSet<QNetworkReply *> m_trackedReplies;
     135              :   QTimer *m_inactivityTimer = nullptr;
     136              :   static constexpr int kInactivityTimeoutMs = 60000;
     137              : };
        

Generated by: LCOV version 2.0-1