data/ContactModels.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QByteArray> | ||
| 4 | #include <QDateTime> | ||
| 5 | #include <QList> | ||
| 6 | #include <QMetaType> | ||
| 7 | #include <QString> | ||
| 8 | #include <QStringList> | ||
| 9 | |||
| 10 | #include "DavPushModels.h" | ||
| 11 | #include "data/ContactPeopleModels.h" | ||
| 12 | |||
| 13 | // Sprint 97 contact value types (S97.1.1, S97.1.2, decision D-1): | ||
| 14 | // one ContactCard is one vCard — a person, an organisation or a group. | ||
| 15 | // Addresses, numbers and groups are values of that card. The raw vCard is | ||
| 16 | // the source of truth; ContactCard is its interpreted view (VCardCodec). | ||
| 17 | |||
| 18 | // EMAIL, TEL, URL and IMPP values. | ||
| 19 | struct ContactValue { | ||
| 20 | 477 | QString value; // unescaped; TEL without a "tel:" URI prefix | |
| 21 | 477 | QStringList types; // lower case ("work", "home", "cell"), without "pref"/"internet" | |
| 22 | 477 | bool preferred = false; | |
| 23 | 477 | QString label; // Apple/Google X-ABLabel of the property's group | |
| 24 | // Position of the property in the parsed card; editors keep it so the | ||
| 25 | // codec patches exactly that line (-1 = a value the user added). | ||
| 26 | 477 | int sourceIndex = -1; | |
| 27 |
5/10✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 477 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 477 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 477 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 477 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 477 times.
|
477 | bool operator==(const ContactValue &) const = default; |
| 28 | }; | ||
| 29 | |||
| 30 | // ADR (structured postal address). | ||
| 31 | struct ContactAddress { | ||
| 32 | 117 | QString poBox; | |
| 33 | 117 | QString extended; | |
| 34 | 117 | QString street; | |
| 35 | 117 | QString locality; | |
| 36 | 117 | QString region; | |
| 37 | 117 | QString postalCode; | |
| 38 | 117 | QString country; | |
| 39 | 117 | QStringList types; | |
| 40 | 117 | bool preferred = false; | |
| 41 | 117 | QString label; | |
| 42 | 117 | GeoPoint geo; // Sprint 103: a pinned position (ADR;GEO="geo:…") | |
| 43 | 117 | int sourceIndex = -1; | |
| 44 | bool isEmpty() const; | ||
| 45 | // "Hauptstr. 1, 10115 Berlin, Germany" — the card shows one line. | ||
| 46 | QString oneLine() const; | ||
| 47 |
12/24✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 117 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 117 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 117 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 117 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 117 times.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 117 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 117 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 117 times.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 117 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 117 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 117 times.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 117 times.
|
117 | bool operator==(const ContactAddress &) const = default; |
| 48 | }; | ||
| 49 | |||
| 50 | // BDAY / ANNIVERSARY; a date without a year keeps year 0 (D-2, "Year unknown"). | ||
| 51 | struct PartialDate { | ||
| 52 | 53312 | int year = 0; | |
| 53 | 53056 | int month = 0; | |
| 54 | 52960 | int day = 0; | |
| 55 |
7/8✓ Branch 2 → 3 taken 6941 times.
✓ Branch 2 → 7 taken 67627 times.
✓ Branch 3 → 4 taken 6931 times.
✓ Branch 3 → 7 taken 10 times.
✓ Branch 4 → 5 taken 6928 times.
✓ Branch 4 → 7 taken 3 times.
✓ Branch 5 → 6 taken 6928 times.
✗ Branch 5 → 7 not taken.
|
74568 | bool isValid() const { return month >= 1 && month <= 12 && day >= 1 && day <= 31; } |
| 56 | 2639 | bool hasYear() const { return year > 0; } | |
| 57 | // "1985-04-12" or "--04-12". | ||
| 58 | QString toIso() const; | ||
| 59 | static PartialDate fromIso(const QString &text); | ||
| 60 |
5/6✓ Branch 2 → 3 taken 256 times.
✓ Branch 2 → 4 taken 53056 times.
✓ Branch 4 → 5 taken 96 times.
✓ Branch 4 → 6 taken 52960 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 52960 times.
|
53312 | bool operator==(const PartialDate &) const = default; |
| 61 | }; | ||
| 62 | |||
| 63 | // Sprint 103 (D-9): a labelled date (itemN.X-ABDATE + X-ABLabel). | ||
| 64 | struct ContactDate { | ||
| 65 | 394 | QString label; | |
| 66 | 394 | PartialDate date; | |
| 67 | 394 | int sourceIndex = -1; | |
| 68 |
4/6✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 394 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 394 times.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 10 taken 393 times.
|
394 | bool operator==(const ContactDate &) const = default; |
| 69 | }; | ||
| 70 | |||
| 71 | // N (RFC 6350 §6.2.2). | ||
| 72 | struct ContactName { | ||
| 73 | 26540 | QString family; | |
| 74 | 24398 | QString given; | |
| 75 | 24299 | QString additional; | |
| 76 | 24299 | QString prefix; | |
| 77 | 24299 | QString suffix; | |
| 78 | bool isEmpty() const; | ||
| 79 |
7/10✓ Branch 3 → 4 taken 2142 times.
✓ Branch 3 → 5 taken 24398 times.
✓ Branch 6 → 7 taken 99 times.
✓ Branch 6 → 8 taken 24299 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 24299 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 24299 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 24299 times.
|
26540 | bool operator==(const ContactName &) const = default; |
| 80 | }; | ||
| 81 | |||
| 82 | // PHOTO: inline data, a URI (shown only on the account's own origin, D-15) | ||
| 83 | // or none. | ||
| 84 | struct ContactPhoto { | ||
| 85 | enum class Kind { None, Inline, Uri }; | ||
| 86 | 26458 | Kind kind = Kind::None; | |
| 87 | 26126 | QByteArray data; // decoded image bytes (Inline) | |
| 88 | 26125 | QString mimeType; // "image/jpeg", "image/png" (best effort) | |
| 89 | 26125 | QString uri; // Uri | |
| 90 |
6/8✓ Branch 2 → 3 taken 332 times.
✓ Branch 2 → 4 taken 26126 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 26125 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 26125 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 26125 times.
|
26458 | bool operator==(const ContactPhoto &) const = default; |
| 91 | }; | ||
| 92 | |||
| 93 | // A known property MailJD shows read-only (PRONOUNS, …): label and | ||
| 94 | // display value. | ||
| 95 | struct ContactExtraField { | ||
| 96 | ✗ | QString name; // property name, e.g. "IMPP" | |
| 97 | ✗ | QString label; // "Skype", "spouse" … | |
| 98 | ✗ | QString value; | |
| 99 | ✗ | bool operator==(const ContactExtraField &) const = default; | |
| 100 | }; | ||
| 101 | |||
| 102 | struct ContactCard { | ||
| 103 | 6 | QString uid; | |
| 104 | 6 | QString version; // "2.1", "3.0" or "4.0" | |
| 105 | 6 | QString kind = QStringLiteral("individual"); // individual, group, org, location, family (X-MAILJD-KIND) | |
| 106 | 6 | QString formattedName; // FN as stored (may be empty) | |
| 107 | 6 | ContactName name; | |
| 108 | 6 | QString nickname; | |
| 109 | 6 | QList<ContactValue> emails; | |
| 110 | 6 | QList<ContactValue> phones; | |
| 111 | 6 | QList<ContactValue> urls; | |
| 112 | 6 | QList<ContactAddress> addresses; | |
| 113 | 6 | QString organization; // ORG first component | |
| 114 | 6 | QString department; // ORG second component | |
| 115 | 6 | QString title; | |
| 116 | 6 | PartialDate birthday; | |
| 117 | 6 | PartialDate anniversary; | |
| 118 | 6 | QString note; | |
| 119 | 6 | QStringList categories; // CATEGORIES (Nextcloud groups, D-7) | |
| 120 | 6 | QStringList members; // MEMBER / X-ADDRESSBOOKSERVER-MEMBER URIs | |
| 121 | 6 | ContactPhoto photo; | |
| 122 | 6 | QList<ContactExtraField> extraFields; | |
| 123 | // Sprint 103 people details (VCardPeople.cpp / VCardPeoplePatch.cpp). | ||
| 124 | 6 | QList<ContactRelation> relations; | |
| 125 | 5 | ContactMet met; | |
| 126 | 5 | QList<ContactSocial> social; | |
| 127 | 5 | QStringList languages; // LANG in preference order | |
| 128 | 5 | QStringList interests; // INTEREST (HOBBY/EXPERTISE read as interests) | |
| 129 | 5 | QList<ContactDate> otherDates; | |
| 130 | 5 | QString birthplace; | |
| 131 | 5 | GeoPoint geo; // card GEO | |
| 132 | 5 | QString timeZone; // TZ as stored (read-only) | |
| 133 | 5 | QString gender; // GENDER sex component as stored ("F", "Female", …; D-4 labels, F103-41) | |
| 134 | 5 | QString genderIdentity; // GENDER identity component (read-only, kept on write) | |
| 135 | 5 | QList<ContactMailLink> mailLinks; | |
| 136 | 25198 | bool isGroup() const { return kind == QLatin1String("group"); } | |
| 137 | // A family's own entry: written as X-MAILJD-KIND:family (other clients see | ||
| 138 | // an individual card). Its members are its RELATED member lines; it has no | ||
| 139 | // other relationships. | ||
| 140 | 53245 | bool isFamily() const { return kind == QLatin1String("family"); } | |
| 141 |
33/64✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 6 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 6 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 6 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 6 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 6 times.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 6 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 6 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 6 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 6 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 6 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 6 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 6 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 6 times.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 6 times.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 6 times.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 6 times.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 6 times.
✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 6 times.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 6 times.
✗ Branch 60 → 61 not taken.
✓ Branch 60 → 62 taken 6 times.
✓ Branch 63 → 64 taken 1 time.
✓ Branch 63 → 65 taken 5 times.
✗ Branch 66 → 67 not taken.
✓ Branch 66 → 68 taken 5 times.
✗ Branch 69 → 70 not taken.
✓ Branch 69 → 71 taken 5 times.
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 5 times.
✗ Branch 75 → 76 not taken.
✓ Branch 75 → 77 taken 5 times.
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 80 taken 5 times.
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 5 times.
✗ Branch 84 → 85 not taken.
✓ Branch 84 → 86 taken 5 times.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 5 times.
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 92 taken 5 times.
✗ Branch 93 → 94 not taken.
✓ Branch 93 → 95 taken 5 times.
✗ Branch 96 → 97 not taken.
✓ Branch 96 → 98 taken 5 times.
|
6 | bool operator==(const ContactCard &) const = default; |
| 142 | }; | ||
| 143 | Q_DECLARE_METATYPE(ContactCard) | ||
| 144 | |||
| 145 | // An address book as discovered on a server (CardDavClient) and as kept in | ||
| 146 | // the store (S97.1.2/S97.1.3). The local book has accountId "" and path | ||
| 147 | // "local" ("On this computer"). | ||
| 148 | struct AddressBookInfo { | ||
| 149 | qint64 id = 0; // store id (0 before it is stored) | ||
| 150 | QString accountId; | ||
| 151 | QString path; // DAV href path | ||
| 152 | QString displayName; // server display name (may be empty) | ||
| 153 | bool readOnly = false; // no write privilege (RFC 3744) | ||
| 154 | QString ctag; | ||
| 155 | QString syncToken; // RFC 6578 token of the last applied sync | ||
| 156 | bool supportsSyncCollection = false; | ||
| 157 | QStringList supportedVersions; // supported-address-data, e.g. "3.0", "4.0" | ||
| 158 | qint64 maxResourceSize = 0; // 0 = not announced | ||
| 159 | bool fetched = false; // one complete fetch done (D-5) | ||
| 160 | QDateTime lastSync; | ||
| 161 | QString lastError; // technical; shown only behind "Details" | ||
| 162 | int skippedCards = 0; // cards of the last sync that could not be read | ||
| 163 | bool showInAll = true; // per-book "Show in All contacts" | ||
| 164 | bool useForCompletion = true; // per-book "Use for address completion" | ||
| 165 | DavPushOffer push; // Sprint 96: read with the book's properties; not stored | ||
| 166 |
3/4✓ Branch 3 → 4 taken 10387 times.
✓ Branch 3 → 8 taken 3415 times.
✓ Branch 6 → 7 taken 10387 times.
✗ Branch 6 → 8 not taken.
|
13802 | bool isLocal() const { return accountId.isEmpty() && path == QLatin1String("local"); } |
| 167 | }; | ||
| 168 |
4/7✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 14 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
✗ Branch 14 → 15 not taken.
|
1 | Q_DECLARE_METATYPE(AddressBookInfo) |
| 169 | |||
| 170 | // One stored card: the raw vCard plus its server identity and sync state. | ||
| 171 | struct StoredCard { | ||
| 172 | qint64 id = 0; | ||
| 173 | qint64 bookId = 0; | ||
| 174 | QString uid; | ||
| 175 | QString href; // resource URL or path on the server ("" for local cards) | ||
| 176 | QString etag; // ETag of base_raw | ||
| 177 | QString raw; // the card as MailJD shows it (local edits applied) | ||
| 178 | QString baseRaw; // the card as last seen on the server (merge base) | ||
| 179 | QString syncState; // "", "pending", "conflict", "legacy" (read-only until refetched) | ||
| 180 | bool writable = true; | ||
| 181 | }; | ||
| 182 | |||
| 183 | // Write-back queue entry of one card (S97.1.4, the CalendarOutbox contract). | ||
| 184 | struct ContactOutboxEntry { | ||
| 185 | qint64 id = 0; | ||
| 186 | qint64 bookId = 0; | ||
| 187 | QString uid; | ||
| 188 | QString operation; // "put" or "delete" | ||
| 189 | QString href; | ||
| 190 | QString baseEtag; // empty = create (If-None-Match: *) | ||
| 191 | int revision = 1; // bumped by every local change while queued | ||
| 192 | int attempts = 0; | ||
| 193 | qint64 nextAttempt = 0; // epoch seconds | ||
| 194 | QString lastError; | ||
| 195 | QString state = QStringLiteral("pending"); // "pending" or "conflict" | ||
| 196 | QString serverRaw; // conflict: the server version | ||
| 197 | QString serverEtag; | ||
| 198 | QStringList conflictFields; | ||
| 199 | qint64 dependsOn = 0; // outbox id that must finish first (move: delete after create) | ||
| 200 | QString baseRaw; // move source (D-22): the card's last server version | ||
| 201 | }; | ||
| 202 |