presentation/QuickRuleMailFlow.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Mail-flow half of QuickRulesBackend (Sprint 95 Phase 4): rule from the | ||
| 2 | // displayed mail (S95.4.1) or the current search (S95.4.4), the live match | ||
| 3 | // preview and "Also apply to existing messages" (S95.4.6, D-11), and the | ||
| 4 | // existing-rule awareness of every creation path (S95.4.9, principle 8). | ||
| 5 | #include "QuickRulesBackend.h" | ||
| 6 | |||
| 7 | #include "RuleConsoleInput.h" | ||
| 8 | #include "RuleDraftFactory.h" | ||
| 9 | #include "RuleEditorFields.h" | ||
| 10 | #include "RuleSentence.h" | ||
| 11 | |||
| 12 | #include <QElapsedTimer> | ||
| 13 | #include <QLoggingCategory> | ||
| 14 | #include <QTimer> | ||
| 15 | |||
| 16 | Q_DECLARE_LOGGING_CATEGORY(lcRulesBackend) | ||
| 17 | |||
| 18 | namespace { | ||
| 19 | |||
| 20 | constexpr int kApplyLimit = 5000; | ||
| 21 | // S106-323: preview and apply evaluate every cached header of the scope on | ||
| 22 | // the GUI thread; a pattern that is slow on the mail text (a :regex that | ||
| 23 | // backtracks up to PCRE2's match limit per value) stops the pass here. | ||
| 24 | constexpr qint64 kEvaluationBudgetMs = 2000; | ||
| 25 | |||
| 26 | // Same rule shape: same field (header list, address part) and match type. | ||
| 27 | 13 | bool sameShape(const Sieve::Condition &a, const Sieve::Condition &b) { | |
| 28 | 13 | QStringList ah, bh; | |
| 29 |
2/2✓ Branch 10 → 4 taken 13 times.
✓ Branch 10 → 11 taken 13 times.
|
26 | for (const QString &h : a.headers) |
| 30 |
2/2✓ Branch 5 → 6 taken 13 times.
✓ Branch 6 → 7 taken 13 times.
|
13 | ah.append(h.toLower()); |
| 31 |
2/2✓ Branch 19 → 13 taken 13 times.
✓ Branch 19 → 20 taken 13 times.
|
26 | for (const QString &h : b.headers) |
| 32 |
2/2✓ Branch 14 → 15 taken 13 times.
✓ Branch 15 → 16 taken 13 times.
|
13 | bh.append(h.toLower()); |
| 33 |
4/7✓ Branch 21 → 22 taken 12 times.
✓ Branch 22 → 23 taken 12 times.
✗ Branch 22 → 31 not taken.
✓ Branch 24 → 25 taken 12 times.
✗ Branch 24 → 31 not taken.
✓ Branch 26 → 27 taken 12 times.
✗ Branch 26 → 31 not taken.
|
12 | return a.kind == b.kind && ah == bh && a.addressPart == b.addressPart && a.matchType == b.matchType && |
| 34 |
4/6✓ Branch 20 → 21 taken 12 times.
✓ Branch 20 → 31 taken 1 time.
✓ Branch 27 → 28 taken 12 times.
✗ Branch 27 → 31 not taken.
✓ Branch 29 → 30 taken 12 times.
✗ Branch 29 → 31 not taken.
|
38 | a.negated == b.negated && a.comparator == b.comparator; |
| 35 | 13 | } | |
| 36 | |||
| 37 | } // namespace | ||
| 38 | |||
| 39 | 95 | QString QuickRulesBackend::sieveNameFor(const QString &imapPath) const { | |
| 40 |
2/2✓ Branch 2 → 3 taken 95 times.
✓ Branch 3 → 4 taken 95 times.
|
95 | const QString name = mailboxDirectory().sieveName(imapPath); |
| 41 |
2/2✓ Branch 6 → 7 taken 8 times.
✓ Branch 6 → 8 taken 87 times.
|
190 | return name.isEmpty() ? imapPath : name; |
| 42 | 95 | } | |
| 43 | |||
| 44 | 7 | QString QuickRulesBackend::imapPathFor(const QString &sieveName) const { | |
| 45 |
2/2✓ Branch 2 → 3 taken 7 times.
✓ Branch 3 → 4 taken 7 times.
|
7 | const QString path = mailboxDirectory().imapPath(sieveName); |
| 46 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 7 times.
|
14 | return path.isEmpty() ? sieveName : path; |
| 47 | 7 | } | |
| 48 | |||
| 49 | 937 | RuleMatcher::Message QuickRulesBackend::messageFor(const MailHeader &header, | |
| 50 | const QMap<QString, QString> &fields) const { | ||
| 51 | 937 | RuleMatcher::Message message; | |
| 52 |
1/1✓ Branch 2 → 3 taken 937 times.
|
937 | message.allHeaders = !fields.isEmpty(); |
| 53 |
2/2✓ Branch 3 → 4 taken 709 times.
✓ Branch 3 → 5 taken 228 times.
|
937 | message.size = header.size > 0 ? header.size : -1; |
| 54 |
3/5✓ Branch 11 → 12 taken 937 times.
✓ Branch 16 → 17 taken 937 times.
✓ Branch 16 → 18 taken 937 times.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
|
1874 | message.headers.insert(QStringLiteral("from"), {header.from}); |
| 55 |
3/5✓ Branch 23 → 24 taken 937 times.
✓ Branch 28 → 29 taken 937 times.
✓ Branch 28 → 30 taken 937 times.
✗ Branch 130 → 131 not taken.
✗ Branch 130 → 132 not taken.
|
1874 | message.headers.insert(QStringLiteral("to"), {header.to}); |
| 56 |
3/5✓ Branch 35 → 36 taken 937 times.
✓ Branch 40 → 41 taken 937 times.
✓ Branch 40 → 42 taken 937 times.
✗ Branch 149 → 150 not taken.
✗ Branch 149 → 151 not taken.
|
1874 | message.headers.insert(QStringLiteral("subject"), {header.subject}); |
| 57 | // S95.1.11: rows synced since schema v24 also know every Cc address and | ||
| 58 | // the List-Id; an empty list means "known to be absent". | ||
| 59 |
2/2✓ Branch 42 → 43 taken 1 time.
✓ Branch 42 → 83 taken 936 times.
|
937 | if (header.recipientsKnown) { |
| 60 |
3/11✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 46 not taken.
✓ Branch 51 → 52 taken 1 time.
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 59 taken 1 time.
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
✗ Branch 167 → 168 not taken.
✗ Branch 167 → 171 not taken.
✗ Branch 169 → 170 not taken.
✗ Branch 169 → 171 not taken.
|
2 | message.headers.insert(QStringLiteral("cc"), header.cc.isEmpty() ? QStringList{} : QStringList{header.cc}); |
| 61 |
1/1✓ Branch 72 → 73 taken 1 time.
|
1 | message.headers.insert(QStringLiteral("list-id"), |
| 62 |
6/15✗ Branch 60 → 61 not taken.
✓ Branch 60 → 62 taken 1 time.
✓ Branch 66 → 67 taken 1 time.
✓ Branch 76 → 77 taken 1 time.
✗ Branch 76 → 80 not taken.
✓ Branch 78 → 79 taken 1 time.
✓ Branch 78 → 80 taken 1 time.
✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 82 not taken.
✗ Branch 188 → 189 not taken.
✗ Branch 188 → 192 not taken.
✗ Branch 190 → 191 not taken.
✗ Branch 190 → 192 not taken.
✗ Branch 194 → 195 not taken.
✗ Branch 194 → 196 not taken.
|
4 | header.listId.isEmpty() ? QStringList{} : QStringList{QLatin1Char('<') + header.listId + QLatin1Char('>')}); |
| 63 | } | ||
| 64 |
4/4✓ Branch 83 → 84 taken 937 times.
✓ Branch 97 → 98 taken 2306 times.
✓ Branch 99 → 85 taken 1369 times.
✓ Branch 99 → 100 taken 937 times.
|
2306 | for (auto it = fields.cbegin(); it != fields.cend(); ++it) |
| 65 |
4/6✓ Branch 89 → 90 taken 1369 times.
✓ Branch 90 → 91 taken 1369 times.
✓ Branch 94 → 95 taken 1369 times.
✓ Branch 94 → 96 taken 1369 times.
✗ Branch 215 → 216 not taken.
✗ Branch 215 → 217 not taken.
|
2738 | message.headers.insert(it.key().toLower(), {it.value()}); |
| 66 | 937 | return message; | |
| 67 | 4183 | } | |
| 68 | |||
| 69 | // --- rule from the displayed mail ------------------------------------------------- | ||
| 70 | |||
| 71 | 15 | QVariantMap QuickRulesBackend::mailRuleContext() { | |
| 72 |
2/3✓ Branch 3 → 4 taken 15 times.
✗ Branch 3 → 5 not taken.
✓ Branch 4 → 6 taken 15 times.
|
15 | const auto mail = m_deps.displayedMail ? m_deps.displayedMail() : Deps::DisplayedMail{}; |
| 73 |
2/3✓ Branch 7 → 8 taken 15 times.
✗ Branch 7 → 9 not taken.
✓ Branch 8 → 10 taken 15 times.
|
15 | const auto account = m_deps.account ? m_deps.account() : std::nullopt; |
| 74 | 15 | QStringList own; | |
| 75 |
1/2✓ Branch 11 → 12 taken 15 times.
✗ Branch 11 → 21 not taken.
|
15 | if (account) |
| 76 |
2/4✓ Branch 18 → 19 taken 30 times.
✓ Branch 18 → 20 taken 15 times.
✗ Branch 240 → 241 not taken.
✗ Branch 240 → 242 not taken.
|
45 | own = {account->email, account->imap.username}; |
| 77 |
1/1✓ Branch 21 → 22 taken 15 times.
|
15 | const RuleDraftFactory::MailFacts facts = RuleDraftFactory::factsFromHeaders(mail.headers); |
| 78 |
1/1✓ Branch 22 → 23 taken 15 times.
|
15 | const QList<RuleDraftFactory::Chip> chips = RuleDraftFactory::chips(facts, own); |
| 79 | // Folder: the prediction, else the mail's folder unless it is the inbox. | ||
| 80 | 15 | QString folder = mail.predictedFolder; | |
| 81 |
4/6✓ Branch 25 → 26 taken 9 times.
✓ Branch 25 → 30 taken 6 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 9 times.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 15 times.
|
15 | if (folder.isEmpty() && mail.folderPath.compare(QLatin1String("INBOX"), Qt::CaseInsensitive) != 0) |
| 82 | ✗ | folder = mail.folderPath; | |
| 83 |
1/1✓ Branch 33 → 34 taken 15 times.
|
15 | const SieveSession *s = session(); |
| 84 |
4/7✓ Branch 38 → 39 taken 15 times.
✗ Branch 38 → 43 not taken.
✓ Branch 39 → 40 taken 15 times.
✗ Branch 39 → 43 not taken.
✓ Branch 40 → 41 taken 15 times.
✓ Branch 41 → 42 taken 15 times.
✗ Branch 41 → 43 not taken.
|
30 | QVariantMap context{{QStringLiteral("available"), available() && s != nullptr && !readOnly()}, |
| 85 |
1/1✓ Branch 48 → 49 taken 15 times.
|
30 | {QStringLiteral("chips"), RuleDraftFactory::chipMaps(chips)}, |
| 86 |
3/3✓ Branch 54 → 55 taken 9 times.
✓ Branch 54 → 56 taken 6 times.
✓ Branch 56 → 57 taken 6 times.
|
30 | {QStringLiteral("folder"), folder.isEmpty() ? QString() : sieveNameFor(folder)}, |
| 87 | 30 | {QStringLiteral("predicted"), !mail.predictedFolder.isEmpty()}, | |
| 88 |
2/6✗ Branch 67 → 68 not taken.
✓ Branch 67 → 71 taken 15 times.
✗ Branch 126 → 127 not taken.
✓ Branch 126 → 128 taken 15 times.
✗ Branch 296 → 297 not taken.
✗ Branch 296 → 298 not taken.
|
30 | {QStringLiteral("scopePath"), mail.folderPath.isEmpty() ? QStringLiteral("INBOX") : mail.folderPath}, |
| 89 |
3/7✗ Branch 77 → 78 not taken.
✓ Branch 77 → 81 taken 15 times.
✓ Branch 82 → 83 taken 15 times.
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 123 taken 15 times.
✗ Branch 282 → 283 not taken.
✗ Branch 282 → 284 not taken.
|
30 | {QStringLiteral("scopeName"), sieveNameFor(mail.folderPath.isEmpty() ? QStringLiteral("INBOX") : mail.folderPath)}, |
| 90 |
2/4✓ Branch 87 → 88 taken 15 times.
✗ Branch 87 → 91 not taken.
✓ Branch 89 → 90 taken 15 times.
✗ Branch 89 → 91 not taken.
|
30 | {QStringLiteral("stop"), s && s->document().style.stopByDefault}, |
| 91 | 30 | {QStringLiteral("sender"), facts.sender}, | |
| 92 |
5/7✓ Branch 101 → 102 taken 9 times.
✓ Branch 101 → 106 taken 6 times.
✓ Branch 105 → 107 taken 9 times.
✓ Branch 110 → 111 taken 135 times.
✓ Branch 110 → 112 taken 15 times.
✗ Branch 250 → 251 not taken.
✗ Branch 250 → 252 not taken.
|
174 | {QStringLiteral("name"), facts.listId.isEmpty() ? facts.sender.section(QLatin1Char('@'), 1) : facts.listId}}; |
| 93 |
2/4✓ Branch 140 → 141 taken 15 times.
✗ Branch 140 → 142 not taken.
✗ Branch 141 → 142 not taken.
✓ Branch 141 → 143 taken 15 times.
|
15 | if (!s || mail.uid < 0) |
| 94 | ✗ | return context; | |
| 95 | // (a) Already handled: rules that would act on this mail now. | ||
| 96 | 15 | MailHeader header; | |
| 97 |
1/1✓ Branch 148 → 149 taken 15 times.
|
30 | header.from = mail.headers.value(QStringLiteral("from")); |
| 98 |
1/1✓ Branch 158 → 159 taken 15 times.
|
30 | header.to = mail.headers.value(QStringLiteral("to")); |
| 99 |
1/1✓ Branch 168 → 169 taken 15 times.
|
30 | header.subject = mail.headers.value(QStringLiteral("subject")); |
| 100 |
2/2✓ Branch 174 → 175 taken 15 times.
✓ Branch 176 → 177 taken 15 times.
|
15 | const QList<int> hits = RuleMatcher::matchingBlocks(s->document(), messageFor(header, mail.headers)); |
| 101 |
2/2✓ Branch 228 → 180 taken 5 times.
✓ Branch 228 → 229 taken 10 times.
|
15 | for (const int index : hits) { |
| 102 | 5 | const Sieve::Block &block = s->document().blocks.at(index); | |
| 103 |
2/3✓ Branch 183 → 184 taken 5 times.
✗ Branch 186 → 187 not taken.
✓ Branch 186 → 188 taken 5 times.
|
5 | if (RuleSentence::target(block.rule).isEmpty()) |
| 104 | ✗ | continue; | |
| 105 |
1/1✓ Branch 209 → 210 taken 5 times.
|
10 | context.insert(QStringLiteral("handledBy"), |
| 106 | 15 | QVariantMap{{QStringLiteral("blockIndex"), index}, | |
| 107 |
1/3✓ Branch 195 → 196 taken 5 times.
✗ Branch 195 → 197 not taken.
✗ Branch 197 → 198 not taken.
|
10 | {QStringLiteral("name"), block.named ? block.name : RuleSentence::generatedName(block.rule)}, |
| 108 |
3/5✓ Branch 202 → 203 taken 5 times.
✓ Branch 215 → 216 taken 15 times.
✓ Branch 215 → 217 taken 5 times.
✗ Branch 390 → 391 not taken.
✗ Branch 390 → 392 not taken.
|
25 | {QStringLiteral("target"), RuleSentence::target(block.rule)}}); |
| 109 | 5 | break; | |
| 110 | } | ||
| 111 | 15 | return context; | |
| 112 | 200 | } | |
| 113 | |||
| 114 | 1 | QVariantMap QuickRulesBackend::searchRuleContext(const QString &query) { | |
| 115 | 1 | QStringList skipped; | |
| 116 |
1/1✓ Branch 2 → 3 taken 1 time.
|
1 | const SieveSession *s = session(); |
| 117 |
4/8✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 11 not taken.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 11 not taken.
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 14 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 16 not taken.
|
2 | const bool body = s && s->capabilities().supports(QStringLiteral("body")); |
| 118 |
1/1✓ Branch 16 → 17 taken 1 time.
|
1 | const QList<QVariantMap> conditions = RuleDraftFactory::conditionsFromSearch(query, body, &skipped); |
| 119 | 1 | QVariantMap draft = RuleDraftFactory::draft(conditions, QString(), false, false, | |
| 120 |
3/5✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 22 not taken.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 22 not taken.
✓ Branch 24 → 25 taken 1 time.
|
2 | s && s->document().style.stopByDefault, QString()); |
| 121 | // A sorting rule is the common case: start with an empty "Move to folder" | ||
| 122 | // the editor focuses (RJ6), so only the folder is left to type. | ||
| 123 |
1/1✓ Branch 46 → 47 taken 1 time.
|
2 | draft.insert(QStringLiteral("actions"), |
| 124 |
4/8✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 54 taken 1 time.
✓ Branch 56 → 57 taken 2 times.
✓ Branch 56 → 58 taken 1 time.
✗ Branch 110 → 111 not taken.
✗ Branch 110 → 112 not taken.
✗ Branch 116 → 117 not taken.
✗ Branch 116 → 118 not taken.
|
7 | QVariantList{QVariantMap{{QStringLiteral("kind"), QStringLiteral("move")}, {QStringLiteral("target"), QString()}}}); |
| 125 | 3 | return {{QStringLiteral("draft"), draft}, {QStringLiteral("skipped"), skipped}, | |
| 126 |
2/4✓ Branch 80 → 81 taken 3 times.
✓ Branch 80 → 82 taken 1 time.
✗ Branch 156 → 157 not taken.
✗ Branch 156 → 158 not taken.
|
6 | {QStringLiteral("usable"), !conditions.isEmpty()}}; |
| 127 | 8 | } | |
| 128 | |||
| 129 | // --- evaluation, awareness, preview -------------------------------------------------- | ||
| 130 | |||
| 131 | 33 | QVariantMap QuickRulesBackend::evaluateDraft(const QVariantMap &draft, const QString &scopePath) { | |
| 132 | 33 | QVariantMap result; | |
| 133 |
1/1✓ Branch 2 → 3 taken 33 times.
|
33 | const SieveSession *s = session(); |
| 134 |
1/1✓ Branch 3 → 4 taken 33 times.
|
33 | const Sieve::Rule rule = prepareRule(draft); |
| 135 |
2/2✓ Branch 4 → 5 taken 33 times.
✓ Branch 9 → 10 taken 33 times.
|
66 | result.insert(QStringLiteral("sentence"), RuleSentence::sentence(rule, RuleSentence::rich)); |
| 136 |
1/2✓ Branch 14 → 15 taken 33 times.
✗ Branch 14 → 125 not taken.
|
33 | if (s) { |
| 137 | 33 | const Sieve::Document &document = s->document(); | |
| 138 |
1/1✓ Branch 16 → 17 taken 33 times.
|
33 | const QString target = RuleSentence::target(rule); |
| 139 |
2/2✓ Branch 122 → 18 taken 270 times.
✓ Branch 122 → 123 taken 33 times.
|
303 | for (int i = 0; i < document.blocks.size(); ++i) { |
| 140 | 270 | const Sieve::Block &block = document.blocks.at(i); | |
| 141 |
4/4✓ Branch 19 → 20 taken 187 times.
✓ Branch 19 → 21 taken 83 times.
✓ Branch 20 → 21 taken 26 times.
✓ Branch 20 → 22 taken 161 times.
|
270 | if (block.kind != Sieve::BlockKind::Rule || !block.rule.enabled) |
| 142 | 109 | continue; | |
| 143 |
1/3✓ Branch 22 → 23 taken 161 times.
✗ Branch 22 → 24 not taken.
✗ Branch 24 → 25 not taken.
|
161 | const QString name = block.named ? block.name : RuleSentence::generatedName(block.rule); |
| 144 | // (b) A rule that files into the same folder with the same condition shape. | ||
| 145 |
7/12✓ Branch 31 → 32 taken 100 times.
✓ Branch 31 → 44 taken 49 times.
✓ Branch 34 → 35 taken 19 times.
✓ Branch 34 → 44 taken 81 times.
✓ Branch 49 → 50 taken 161 times.
✗ Branch 49 → 51 not taken.
✓ Branch 51 → 52 taken 10 times.
✓ Branch 51 → 78 taken 151 times.
✗ Branch 257 → 258 not taken.
✗ Branch 257 → 259 not taken.
✗ Branch 261 → 262 not taken.
✗ Branch 261 → 263 not taken.
|
571 | if (!result.contains(QStringLiteral("addTo")) && !target.isEmpty() && |
| 146 |
5/9✓ Branch 32 → 33 taken 100 times.
✓ Branch 36 → 37 taken 10 times.
✓ Branch 36 → 44 taken 9 times.
✓ Branch 38 → 39 taken 10 times.
✗ Branch 38 → 44 not taken.
✓ Branch 47 → 48 taken 161 times.
✗ Branch 47 → 49 not taken.
✗ Branch 253 → 254 not taken.
✗ Branch 253 → 255 not taken.
|
290 | RuleSentence::target(block.rule) == target && block.rule.conditions.size() == 1 && |
| 147 |
7/8✓ Branch 28 → 29 taken 161 times.
✓ Branch 29 → 30 taken 149 times.
✓ Branch 29 → 44 taken 12 times.
✓ Branch 41 → 42 taken 10 times.
✓ Branch 42 → 43 taken 10 times.
✗ Branch 42 → 44 not taken.
✓ Branch 45 → 46 taken 100 times.
✓ Branch 45 → 47 taken 61 times.
|
481 | rule.conditions.size() == 1 && sameShape(block.rule.conditions.first(), rule.conditions.first())) |
| 148 |
1/1✓ Branch 65 → 66 taken 10 times.
|
40 | result.insert(QStringLiteral("addTo"), QVariantMap{{QStringLiteral("blockIndex"), i}, |
| 149 |
2/4✓ Branch 71 → 72 taken 20 times.
✓ Branch 71 → 73 taken 10 times.
✗ Branch 276 → 277 not taken.
✗ Branch 276 → 278 not taken.
|
50 | {QStringLiteral("name"), name}}); |
| 150 | // (c) An earlier rule with stop that catches the new rule's mails. | ||
| 151 |
10/16✓ Branch 81 → 82 taken 161 times.
✓ Branch 82 → 83 taken 125 times.
✓ Branch 82 → 86 taken 36 times.
✓ Branch 83 → 84 taken 125 times.
✓ Branch 84 → 85 taken 11 times.
✓ Branch 84 → 86 taken 114 times.
✓ Branch 87 → 88 taken 161 times.
✗ Branch 87 → 89 not taken.
✓ Branch 89 → 90 taken 161 times.
✗ Branch 89 → 91 not taken.
✓ Branch 91 → 92 taken 11 times.
✓ Branch 91 → 118 taken 150 times.
✗ Branch 299 → 300 not taken.
✗ Branch 299 → 301 not taken.
✗ Branch 303 → 304 not taken.
✗ Branch 303 → 305 not taken.
|
322 | if (!result.contains(QStringLiteral("shadowedBy")) && RuleMatcher::shadows(block.rule, rule)) |
| 152 |
1/1✓ Branch 105 → 106 taken 11 times.
|
44 | result.insert(QStringLiteral("shadowedBy"), QVariantMap{{QStringLiteral("blockIndex"), i}, |
| 153 |
2/4✓ Branch 111 → 112 taken 22 times.
✓ Branch 111 → 113 taken 11 times.
✗ Branch 318 → 319 not taken.
✗ Branch 318 → 320 not taken.
|
55 | {QStringLiteral("name"), name}}); |
| 154 | 161 | } | |
| 155 | 33 | } | |
| 156 | // Live preview over the cached headers of the scope folder (D-11). | ||
| 157 | 33 | int count = 0; | |
| 158 | 33 | int unknown = 0; | |
| 159 | 33 | bool stopped = false; | |
| 160 |
3/6✓ Branch 126 → 127 taken 33 times.
✗ Branch 126 → 130 not taken.
✓ Branch 128 → 129 taken 33 times.
✗ Branch 128 → 130 not taken.
✓ Branch 131 → 132 taken 33 times.
✗ Branch 131 → 184 not taken.
|
33 | if (m_deps.cachedHeaders && !scopePath.isEmpty()) { |
| 161 | 33 | QElapsedTimer budget; | |
| 162 | 33 | budget.start(); | |
| 163 |
1/1✓ Branch 133 → 134 taken 33 times.
|
33 | const QList<MailHeader> headers = m_deps.cachedHeaders(scopePath); |
| 164 |
2/2✓ Branch 181 → 135 taken 258 times.
✓ Branch 181 → 182 taken 33 times.
|
291 | for (qsizetype i = 0; i < headers.size(); ++i) { |
| 165 |
1/2✗ Branch 136 → 137 not taken.
✓ Branch 136 → 154 taken 258 times.
|
258 | if (budget.elapsed() > kEvaluationBudgetMs) { |
| 166 | ✗ | unknown += int(headers.size() - i); // not evaluated | |
| 167 | ✗ | stopped = true; | |
| 168 | ✗ | qCInfo(lcRulesBackend) << "Rule preview stopped after" << i << "of" << headers.size() << "messages"; | |
| 169 | ✗ | break; | |
| 170 | } | ||
| 171 | 258 | const MailHeader &header = headers.at(i); | |
| 172 | 258 | QMap<QString, QString> fields; | |
| 173 |
2/2✓ Branch 155 → 156 taken 258 times.
✓ Branch 156 → 157 taken 258 times.
|
258 | RuleMatcher::Result match = RuleMatcher::evaluate(rule, messageFor(header, fields)); |
| 174 |
5/6✓ Branch 158 → 159 taken 44 times.
✓ Branch 158 → 162 taken 214 times.
✓ Branch 160 → 161 taken 44 times.
✗ Branch 160 → 162 not taken.
✓ Branch 163 → 164 taken 44 times.
✓ Branch 163 → 173 taken 214 times.
|
258 | if (match == RuleMatcher::Result::Unknown && m_deps.cachedHeaderFields) { |
| 175 |
1/1✓ Branch 164 → 165 taken 44 times.
|
44 | fields = m_deps.cachedHeaderFields(header.folderId, header.uid); |
| 176 |
3/3✓ Branch 167 → 168 taken 44 times.
✓ Branch 168 → 169 taken 41 times.
✓ Branch 168 → 173 taken 3 times.
|
44 | if (!fields.isEmpty()) |
| 177 |
2/2✓ Branch 169 → 170 taken 41 times.
✓ Branch 170 → 171 taken 41 times.
|
41 | match = RuleMatcher::evaluate(rule, messageFor(header, fields)); |
| 178 | } | ||
| 179 |
2/2✓ Branch 173 → 174 taken 38 times.
✓ Branch 173 → 175 taken 220 times.
|
258 | if (match == RuleMatcher::Result::Match) |
| 180 | 38 | ++count; | |
| 181 |
2/2✓ Branch 175 → 176 taken 3 times.
✓ Branch 175 → 177 taken 217 times.
|
220 | else if (match == RuleMatcher::Result::Unknown) |
| 182 | 3 | ++unknown; | |
| 183 | 258 | } | |
| 184 | 33 | } | |
| 185 |
1/1✓ Branch 188 → 189 taken 33 times.
|
66 | result.insert(QStringLiteral("matchCount"), count); |
| 186 |
1/1✓ Branch 196 → 197 taken 33 times.
|
66 | result.insert(QStringLiteral("unknownCount"), unknown); |
| 187 |
1/1✓ Branch 204 → 205 taken 33 times.
|
66 | result.insert(QStringLiteral("stopped"), stopped); |
| 188 |
2/2✓ Branch 208 → 209 taken 33 times.
✓ Branch 213 → 214 taken 33 times.
|
66 | result.insert(QStringLiteral("scopeName"), sieveNameFor(scopePath)); |
| 189 |
2/2✓ Branch 219 → 220 taken 27 times.
✓ Branch 219 → 225 taken 6 times.
|
60 | const bool movable = !rule.actions.isEmpty() && |
| 190 |
3/3✓ Branch 222 → 223 taken 27 times.
✓ Branch 223 → 224 taken 22 times.
✓ Branch 223 → 225 taken 5 times.
|
27 | std::all_of(rule.actions.cbegin(), rule.actions.cend(), [](const Sieve::Action &a) { |
| 191 |
2/2✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 6 taken 10 times.
|
15 | return a.kind == Sieve::Action::Kind::FileInto || a.kind == Sieve::Action::Kind::AddFlag || |
| 192 |
4/6✓ Branch 2 → 3 taken 15 times.
✓ Branch 2 → 6 taken 22 times.
✓ Branch 4 → 5 taken 5 times.
✗ Branch 4 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 5 times.
|
52 | a.kind == Sieve::Action::Kind::RemoveFlag || a.kind == Sieve::Action::Kind::Keep; |
| 193 | 33 | }); | |
| 194 |
5/5✓ Branch 226 → 227 taken 22 times.
✓ Branch 226 → 229 taken 11 times.
✓ Branch 227 → 228 taken 20 times.
✓ Branch 227 → 229 taken 2 times.
✓ Branch 234 → 235 taken 33 times.
|
66 | result.insert(QStringLiteral("canApply"), movable && count > 0); // never forward/delete/reject (D-11) |
| 195 | 33 | return result; | |
| 196 | 96 | } | |
| 197 | |||
| 198 | 8 | QString QuickRulesBackend::applyToExisting(const QVariantMap &draft, const QString &scopePath) { | |
| 199 |
1/1✓ Branch 2 → 3 taken 8 times.
|
8 | const QString requestId = nextRequestId(); |
| 200 | 8 | const bool announceNone = std::exchange(m_announceNextApply, false); | |
| 201 |
1/1✓ Branch 4 → 5 taken 8 times.
|
8 | const Sieve::Rule rule = prepareRule(draft); |
| 202 | 8 | QList<qint64> uids; | |
| 203 | 8 | bool stopped = false; | |
| 204 | 8 | QElapsedTimer budget; | |
| 205 | 8 | budget.start(); | |
| 206 |
1/2✓ Branch 7 → 8 taken 8 times.
✗ Branch 7 → 43 not taken.
|
8 | if (m_deps.cachedHeaders) |
| 207 |
5/5✓ Branch 8 → 9 taken 8 times.
✓ Branch 9 → 10 taken 8 times.
✓ Branch 10 → 11 taken 8 times.
✓ Branch 40 → 12 taken 29 times.
✓ Branch 40 → 41 taken 8 times.
|
37 | for (const MailHeader &header : m_deps.cachedHeaders(scopePath)) { |
| 208 |
1/2✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 29 times.
|
29 | if (budget.elapsed() > kEvaluationBudgetMs) { |
| 209 | ✗ | stopped = true; // S106-323: the matches so far are applied | |
| 210 | ✗ | break; | |
| 211 | } | ||
| 212 |
2/2✓ Branch 17 → 18 taken 29 times.
✓ Branch 18 → 19 taken 29 times.
|
29 | RuleMatcher::Result match = RuleMatcher::evaluate(rule, messageFor(header, {})); |
| 213 |
5/6✓ Branch 21 → 22 taken 14 times.
✓ Branch 21 → 25 taken 15 times.
✓ Branch 23 → 24 taken 14 times.
✗ Branch 23 → 25 not taken.
✓ Branch 26 → 27 taken 14 times.
✓ Branch 26 → 33 taken 15 times.
|
29 | if (match == RuleMatcher::Result::Unknown && m_deps.cachedHeaderFields) |
| 214 |
3/3✓ Branch 27 → 28 taken 14 times.
✓ Branch 28 → 29 taken 14 times.
✓ Branch 29 → 30 taken 14 times.
|
14 | match = RuleMatcher::evaluate(rule, messageFor(header, m_deps.cachedHeaderFields(header.folderId, header.uid))); |
| 215 |
2/2✓ Branch 33 → 34 taken 11 times.
✓ Branch 33 → 35 taken 18 times.
|
29 | if (match == RuleMatcher::Result::Match) |
| 216 |
1/1✓ Branch 34 → 35 taken 11 times.
|
11 | uids.append(header.uid); |
| 217 |
1/2✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 29 times.
|
29 | if (uids.size() >= kApplyLimit) |
| 218 | ✗ | break; | |
| 219 | 8 | } | |
| 220 | 8 | QString moveTarget; | |
| 221 | // S106-321: what the run really changed decides the answer — a rule whose | ||
| 222 | // only actions are never applied retroactively (copy, redirect, discard, | ||
| 223 | // reject) or whose target is the scope itself changes nothing. | ||
| 224 | 8 | bool changed = false; | |
| 225 |
2/2✓ Branch 102 → 45 taken 9 times.
✓ Branch 102 → 103 taken 8 times.
|
17 | for (const Sieve::Action &action : rule.actions) { |
| 226 |
3/4✓ Branch 46 → 47 taken 8 times.
✓ Branch 46 → 49 taken 1 time.
✓ Branch 47 → 48 taken 8 times.
✗ Branch 47 → 49 not taken.
|
9 | if (action.kind == Sieve::Action::Kind::Redirect || action.kind == Sieve::Action::Kind::Discard || |
| 227 |
1/2✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 8 times.
|
8 | action.kind == Sieve::Action::Kind::Reject) |
| 228 | 1 | continue; // never applied retroactively (D-11, G10) | |
| 229 |
3/4✓ Branch 50 → 51 taken 7 times.
✓ Branch 50 → 65 taken 1 time.
✓ Branch 51 → 52 taken 7 times.
✗ Branch 51 → 65 not taken.
|
8 | if (action.kind == Sieve::Action::Kind::FileInto && !action.copy) { |
| 230 | 7 | const QString path = !action.specialUse.isEmpty() | |
| 231 |
1/5✗ Branch 54 → 55 not taken.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 7 times.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
|
7 | ? mailboxDirectory().folderForSpecialUse(action.specialUse) |
| 232 |
2/4✗ Branch 53 → 54 not taken.
✓ Branch 53 → 56 taken 7 times.
✗ Branch 55 → 57 not taken.
✓ Branch 56 → 57 taken 7 times.
|
7 | : imapPathFor(action.target); |
| 233 |
1/2✓ Branch 60 → 61 taken 7 times.
✗ Branch 60 → 62 not taken.
|
7 | if (!path.isEmpty()) |
| 234 | 7 | moveTarget = path; | |
| 235 | 7 | continue; | |
| 236 | 7 | } | |
| 237 |
3/8✓ Branch 66 → 67 taken 1 time.
✗ Branch 66 → 69 not taken.
✗ Branch 67 → 68 not taken.
✓ Branch 67 → 70 taken 1 time.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 73 taken 1 time.
|
1 | if (!m_deps.flagMail || (action.kind != Sieve::Action::Kind::AddFlag && action.kind != Sieve::Action::Kind::RemoveFlag)) |
| 238 | ✗ | continue; | |
| 239 |
4/4✓ Branch 73 → 74 taken 1 time.
✓ Branch 74 → 75 taken 1 time.
✓ Branch 98 → 76 taken 3 times.
✓ Branch 98 → 99 taken 1 time.
|
4 | for (const qint64 uid : uids) |
| 240 |
2/2✓ Branch 95 → 79 taken 3 times.
✓ Branch 95 → 96 taken 3 times.
|
6 | for (const QString &flag : action.flags) { |
| 241 |
4/11✓ Branch 80 → 81 taken 3 times.
✗ Branch 80 → 84 not taken.
✓ Branch 87 → 88 taken 3 times.
✗ Branch 89 → 90 not taken.
✓ Branch 89 → 91 taken 3 times.
✓ Branch 91 → 92 taken 3 times.
✗ Branch 91 → 93 not taken.
✗ Branch 180 → 181 not taken.
✗ Branch 180 → 182 not taken.
✗ Branch 184 → 185 not taken.
✗ Branch 184 → 186 not taken.
|
6 | m_deps.flagMail(scopePath, uid, action.kind == Sieve::Action::Kind::AddFlag ? QStringLiteral("add") : QStringLiteral("remove"), flag); |
| 242 | 3 | changed = true; | |
| 243 | } | ||
| 244 | } | ||
| 245 | // Flags first, then one undoable move batch. | ||
| 246 |
6/8✓ Branch 104 → 105 taken 7 times.
✓ Branch 104 → 112 taken 1 time.
✓ Branch 106 → 107 taken 7 times.
✗ Branch 106 → 112 not taken.
✓ Branch 108 → 109 taken 7 times.
✗ Branch 108 → 112 not taken.
✓ Branch 110 → 111 taken 5 times.
✓ Branch 110 → 112 taken 2 times.
|
8 | const bool moving = !moveTarget.isEmpty() && moveTarget != scopePath && m_deps.moveMails && !uids.isEmpty(); |
| 247 |
2/2✓ Branch 113 → 114 taken 5 times.
✓ Branch 113 → 116 taken 3 times.
|
8 | if (moving) { |
| 248 |
1/1✓ Branch 114 → 115 taken 5 times.
|
5 | m_deps.moveMails(scopePath, uids, moveTarget); |
| 249 | 5 | changed = true; | |
| 250 | } | ||
| 251 |
2/4✓ Branch 117 → 118 taken 8 times.
✗ Branch 117 → 119 not taken.
✗ Branch 118 → 119 not taken.
✓ Branch 118 → 120 taken 8 times.
|
8 | const bool truncated = uids.size() >= kApplyLimit || stopped; |
| 252 |
2/2✓ Branch 121 → 122 taken 5 times.
✓ Branch 121 → 124 taken 3 times.
|
8 | const int applied = changed ? int(uids.size()) : 0; |
| 253 |
9/9✓ Branch 125 → 126 taken 8 times.
✓ Branch 129 → 130 taken 8 times.
✓ Branch 130 → 131 taken 8 times.
✓ Branch 131 → 132 taken 8 times.
✓ Branch 132 → 133 taken 8 times.
✓ Branch 134 → 135 taken 8 times.
✓ Branch 135 → 136 taken 8 times.
✓ Branch 143 → 127 taken 8 times.
✓ Branch 143 → 144 taken 8 times.
|
16 | qCInfo(lcRulesBackend) << "Applied a rule to" << applied << "existing messages of" << uids.size() << "matches;" |
| 254 |
2/3✗ Branch 136 → 137 not taken.
✓ Branch 136 → 138 taken 8 times.
✓ Branch 139 → 140 taken 8 times.
|
8 | << (truncated ? "stopped at the limit" : "all"); |
| 255 |
4/4✓ Branch 145 → 146 taken 6 times.
✓ Branch 145 → 148 taken 2 times.
✓ Branch 146 → 147 taken 1 time.
✓ Branch 146 → 148 taken 5 times.
|
8 | const bool nothingToDo = !uids.isEmpty() && !changed; |
| 256 |
1/5✓ Branch 151 → 152 taken 8 times.
✗ Branch 199 → 200 not taken.
✗ Branch 199 → 201 not taken.
✗ Branch 202 → 203 not taken.
✗ Branch 202 → 204 not taken.
|
8 | QTimer::singleShot(0, this, [this, requestId, applied, moving, moveTarget, announceNone, truncated, nothingToDo] { |
| 257 | 7 | QString text = nothingToDo ? tr("This rule has no action that can be applied to messages already received.") | |
| 258 |
6/10✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 9 taken 2 times.
✓ Branch 5 → 6 taken 4 times.
✓ Branch 7 → 8 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
✓ Branch 10 → 12 taken 3 times.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 62 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
|
21 | : moving ? tr("Existing messages moved to %1: %n", nullptr, applied).arg(sieveNameFor(moveTarget)) |
| 259 |
7/7✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 6 times.
✓ Branch 3 → 10 taken 1 time.
✓ Branch 8 → 10 taken 4 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 14 taken 3 times.
|
24 | : tr("Applied to existing messages: %n", nullptr, applied); |
| 260 |
1/4✗ Branch 14 → 15 not taken.
✓ Branch 14 → 24 taken 7 times.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 24 not taken.
|
7 | if (truncated && applied > 0) |
| 261 | ✗ | text += QLatin1Char(' ') + tr("Only the first %n were changed; apply the rule again for the rest.", nullptr, | |
| 262 | ✗ | applied); | |
| 263 |
3/5✓ Branch 37 → 38 taken 7 times.
✓ Branch 40 → 41 taken 21 times.
✓ Branch 40 → 42 taken 7 times.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
|
49 | emit editFinished(requestId, {{QStringLiteral("ok"), !nothingToDo}, {QStringLiteral("count"), applied}, |
| 264 | 14 | {QStringLiteral("message"), text}}); | |
| 265 |
6/6✓ Branch 48 → 49 taken 3 times.
✓ Branch 48 → 51 taken 4 times.
✓ Branch 49 → 50 taken 2 times.
✓ Branch 49 → 51 taken 1 time.
✓ Branch 50 → 51 taken 1 time.
✓ Branch 50 → 58 taken 1 time.
|
7 | if (applied > 0 || nothingToDo || announceNone) |
| 266 |
6/6✓ Branch 51 → 52 taken 2 times.
✓ Branch 51 → 53 taken 4 times.
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 54 taken 1 time.
✓ Branch 54 → 55 taken 1 time.
✓ Branch 55 → 56 taken 6 times.
|
6 | emit toast(applied > 0 || nothingToDo ? text : tr("No message in the inbox matches this rule."), false); |
| 267 |
1/5✓ Branch 36 → 37 taken 7 times.
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 106 not taken.
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 106 not taken.
|
35 | }); |
| 268 | 8 | return requestId; | |
| 269 | 8 | } | |
| 270 | |||
| 271 | // Incoming-mail rules act on the inbox; a run without matches is announced | ||
| 272 | // too, so the click always gets an answer. | ||
| 273 | 3 | QString QuickRulesBackend::applyRuleNow(int blockIndex) { | |
| 274 | 3 | SieveSession *s = session(); | |
| 275 |
6/8✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 11 not taken.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 11 not taken.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 11 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 19 taken 2 times.
|
5 | if (!s || blockIndex < 0 || blockIndex >= s->document().blocks.size() || |
| 276 |
1/2✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 2 times.
|
2 | s->document().blocks.at(blockIndex).kind != Sieve::BlockKind::Rule) |
| 277 |
2/2✓ Branch 14 → 15 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
|
1 | return refuseEdit(tr("This rule no longer exists.")); |
| 278 |
6/6✓ Branch 19 → 20 taken 2 times.
✓ Branch 23 → 24 taken 2 times.
✓ Branch 24 → 25 taken 2 times.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 29 → 21 taken 2 times.
✓ Branch 29 → 30 taken 2 times.
|
4 | qCInfo(lcRulesBackend) << "Apply rule now: block" << blockIndex; |
| 279 | 2 | m_announceNextApply = true; | |
| 280 |
2/2✓ Branch 33 → 34 taken 2 times.
✓ Branch 34 → 35 taken 2 times.
|
2 | return applyToExisting(draftFor(blockIndex), QStringLiteral("INBOX")); |
| 281 | } | ||
| 282 | |||
| 283 | 4 | QString QuickRulesBackend::addToRule(int blockIndex, const QVariantMap &condition) { | |
| 284 |
1/1✓ Branch 2 → 3 taken 4 times.
|
4 | SieveSession *s = session(); |
| 285 |
6/8✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 11 not taken.
✓ Branch 4 → 5 taken 4 times.
✗ Branch 4 → 11 not taken.
✓ Branch 7 → 8 taken 3 times.
✓ Branch 7 → 11 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 19 taken 3 times.
|
7 | if (!s || blockIndex < 0 || blockIndex >= s->document().blocks.size() || |
| 286 |
1/2✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 3 times.
|
3 | s->document().blocks.at(blockIndex).kind != Sieve::BlockKind::Rule) |
| 287 |
2/2✓ Branch 14 → 15 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
|
1 | return refuseEdit(tr("This rule no longer exists.")); |
| 288 | 3 | Sieve::Document document = s->document(); | |
| 289 |
1/1✓ Branch 21 → 22 taken 3 times.
|
3 | Sieve::Block &block = document.blocks[blockIndex]; |
| 290 |
1/1✓ Branch 22 → 23 taken 3 times.
|
3 | const Sieve::Condition added = RuleEditorFields::toCondition(condition); |
| 291 |
7/8✓ Branch 24 → 25 taken 3 times.
✗ Branch 24 → 28 not taken.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 26 → 27 taken 3 times.
✓ Branch 27 → 28 taken 1 time.
✓ Branch 27 → 29 taken 2 times.
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 36 taken 2 times.
|
3 | if (block.rule.conditions.size() != 1 || !sameShape(block.rule.conditions.first(), added)) |
| 292 |
2/2✓ Branch 31 → 32 taken 1 time.
✓ Branch 32 → 33 taken 1 time.
|
1 | return refuseEdit(tr("The rule checks something else; create a new rule instead.")); |
| 293 |
2/2✓ Branch 46 → 38 taken 2 times.
✓ Branch 46 → 47 taken 2 times.
|
4 | for (const QString &value : added.values) |
| 294 |
2/3✓ Branch 39 → 40 taken 2 times.
✓ Branch 41 → 42 taken 2 times.
✗ Branch 41 → 44 not taken.
|
2 | if (!block.rule.conditions.first().values.contains(value)) |
| 295 |
2/2✓ Branch 42 → 43 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
|
2 | block.rule.conditions.first().values.append(value); |
| 296 | 2 | block.dirty = true; | |
| 297 |
1/2✗ Branch 47 → 48 not taken.
✓ Branch 47 → 52 taken 2 times.
|
2 | if (!block.named) { |
| 298 | ✗ | block.name = RuleSentence::generatedName(block.rule); | |
| 299 | ✗ | block.named = true; | |
| 300 | } | ||
| 301 | 2 | block.rule.name = block.name; | |
| 302 | 4 | return submit({Sieve::encode(document), QStringLiteral("extend"), tr("Rule „%1“ extended").arg(block.name), true}); | |
| 303 | 11 | } | |
| 304 | |||
| 305 | // S95.4.8: which rules match the displayed mail, in script order. | ||
| 306 | 199 | QVariantList QuickRulesBackend::matchingRulesForMail() { | |
| 307 | 199 | QVariantList result; | |
| 308 |
1/1✓ Branch 2 → 3 taken 199 times.
|
199 | const SieveSession *s = session(); |
| 309 |
2/3✓ Branch 4 → 5 taken 199 times.
✗ Branch 4 → 6 not taken.
✓ Branch 5 → 7 taken 199 times.
|
199 | const auto mail = m_deps.displayedMail ? m_deps.displayedMail() : Deps::DisplayedMail{}; |
| 310 |
4/4✓ Branch 7 → 8 taken 181 times.
✓ Branch 7 → 9 taken 18 times.
✓ Branch 8 → 9 taken 18 times.
✓ Branch 8 → 10 taken 163 times.
|
199 | if (!s || mail.uid < 0) |
| 311 | 36 | return result; | |
| 312 | 163 | MailHeader header; | |
| 313 |
1/1✓ Branch 15 → 16 taken 163 times.
|
326 | header.from = mail.headers.value(QStringLiteral("from")); |
| 314 |
1/1✓ Branch 25 → 26 taken 163 times.
|
326 | header.to = mail.headers.value(QStringLiteral("to")); |
| 315 |
1/1✓ Branch 35 → 36 taken 163 times.
|
326 | header.subject = mail.headers.value(QStringLiteral("subject")); |
| 316 | 163 | int skipped = 0; | |
| 317 |
6/6✓ Branch 41 → 42 taken 163 times.
✓ Branch 43 → 44 taken 163 times.
✓ Branch 45 → 46 taken 163 times.
✓ Branch 46 → 47 taken 163 times.
✓ Branch 92 → 48 taken 93 times.
✓ Branch 92 → 93 taken 163 times.
|
256 | for (const int index : RuleMatcher::matchingBlocks(s->document(), messageFor(header, mail.headers), &skipped)) { |
| 318 | 93 | const Sieve::Block &block = s->document().blocks.at(index); | |
| 319 |
1/1✓ Branch 74 → 75 taken 93 times.
|
279 | result.append(QVariantMap{{QStringLiteral("blockIndex"), index}, |
| 320 |
1/3✓ Branch 58 → 59 taken 93 times.
✗ Branch 58 → 60 not taken.
✗ Branch 60 → 61 not taken.
|
186 | {QStringLiteral("name"), block.named ? block.name : RuleSentence::generatedName(block.rule)}, |
| 321 |
1/1✓ Branch 65 → 66 taken 93 times.
|
186 | {QStringLiteral("sentence"), RuleSentence::sentence(block.rule, RuleSentence::rich)}, |
| 322 |
3/5✓ Branch 70 → 71 taken 93 times.
✓ Branch 78 → 79 taken 372 times.
✓ Branch 78 → 80 taken 93 times.
✗ Branch 152 → 153 not taken.
✗ Branch 152 → 154 not taken.
|
651 | {QStringLiteral("position"), positionOf(index)}}); |
| 323 | 163 | } | |
| 324 |
2/2✓ Branch 94 → 95 taken 59 times.
✓ Branch 94 → 110 taken 104 times.
|
163 | if (skipped > 0) |
| 325 |
3/5✓ Branch 101 → 102 taken 59 times.
✓ Branch 105 → 106 taken 59 times.
✓ Branch 105 → 107 taken 59 times.
✗ Branch 202 → 203 not taken.
✗ Branch 202 → 204 not taken.
|
177 | result.append(QVariantMap{{QStringLiteral("advanced"), skipped}}); |
| 326 | 163 | return result; | |
| 327 | 782 | } | |
| 328 | |||
| 329 | // --- console rule mode (S95.4.3) ------------------------------------------------------- | ||
| 330 | |||
| 331 | 8 | QString QuickRulesBackend::consoleSeed() { | |
| 332 |
2/3✓ Branch 3 → 4 taken 8 times.
✗ Branch 3 → 5 not taken.
✓ Branch 4 → 6 taken 8 times.
|
8 | const auto mail = m_deps.displayedMail ? m_deps.displayedMail() : Deps::DisplayedMail{}; |
| 333 |
2/2✓ Branch 6 → 7 taken 8 times.
✓ Branch 7 → 8 taken 8 times.
|
16 | return RuleConsoleInput::seedFromSender(RuleDraftFactory::factsFromHeaders(mail.headers).sender); |
| 334 | 8 | } | |
| 335 | |||
| 336 | 1 | QString QuickRulesBackend::completeConsoleRule(const QString &text) const { | |
| 337 |
2/2✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
|
1 | return RuleConsoleInput::completeFolder(text, folderChoices()); |
| 338 | } | ||
| 339 | |||
| 340 | 23 | QVariantMap QuickRulesBackend::parseConsoleRule(const QString &text) { | |
| 341 |
1/1✓ Branch 2 → 3 taken 23 times.
|
23 | const SieveSession *s = session(); |
| 342 |
3/5✓ Branch 3 → 4 taken 23 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 23 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 23 times.
|
23 | const RuleConsoleInput::Result parsed = RuleConsoleInput::parse(text, s && s->document().style.stopByDefault); |
| 343 | 69 | QVariantMap result{{QStringLiteral("ok"), parsed.ok}, {QStringLiteral("applyNow"), parsed.applyNow}, | |
| 344 |
2/4✓ Branch 23 → 24 taken 69 times.
✓ Branch 23 → 25 taken 23 times.
✗ Branch 260 → 261 not taken.
✗ Branch 260 → 262 not taken.
|
115 | {QStringLiteral("forwardTargets"), parsed.forwardTargets}}; |
| 345 |
2/2✓ Branch 31 → 32 taken 8 times.
✓ Branch 31 → 41 taken 15 times.
|
23 | if (!parsed.ok) { |
| 346 |
1/1✓ Branch 36 → 37 taken 8 times.
|
16 | result.insert(QStringLiteral("error"), parsed.error); |
| 347 | 8 | return result; | |
| 348 | } | ||
| 349 |
1/1✓ Branch 41 → 42 taken 15 times.
|
15 | const Sieve::Rule rule = prepareRule(parsed.draft); |
| 350 |
2/3✓ Branch 43 → 44 taken 15 times.
✗ Branch 43 → 45 not taken.
✓ Branch 44 → 46 taken 15 times.
|
15 | const auto mail = m_deps.displayedMail ? m_deps.displayedMail() : Deps::DisplayedMail{}; |
| 351 |
2/4✗ Branch 47 → 48 not taken.
✓ Branch 47 → 51 taken 15 times.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 15 times.
|
15 | const QString scope = mail.folderPath.isEmpty() ? QStringLiteral("INBOX") : mail.folderPath; |
| 352 |
1/1✓ Branch 54 → 55 taken 15 times.
|
15 | const QVariantMap evaluation = evaluateDraft(parsed.draft, scope); |
| 353 | 15 | QStringList preview; | |
| 354 |
4/4✓ Branch 55 → 56 taken 15 times.
✓ Branch 57 → 58 taken 15 times.
✓ Branch 58 → 59 taken 15 times.
✓ Branch 59 → 60 taken 15 times.
|
30 | preview.append(tr("When: %1").arg(RuleSentence::conditions(rule))); |
| 355 |
4/4✓ Branch 63 → 64 taken 15 times.
✓ Branch 65 → 66 taken 15 times.
✓ Branch 66 → 67 taken 15 times.
✓ Branch 67 → 68 taken 15 times.
|
30 | preview.append(tr("Then: %1").arg(RuleSentence::actions(rule))); |
| 356 |
2/2✓ Branch 75 → 76 taken 15 times.
✓ Branch 76 → 77 taken 15 times.
|
30 | const int count = evaluation.value(QStringLiteral("matchCount")).toInt(); |
| 357 |
2/2✓ Branch 81 → 82 taken 2 times.
✓ Branch 81 → 91 taken 13 times.
|
15 | if (parsed.applyNow) |
| 358 |
4/4✓ Branch 82 → 83 taken 2 times.
✓ Branch 84 → 85 taken 2 times.
✓ Branch 85 → 86 taken 2 times.
✓ Branch 86 → 87 taken 2 times.
|
4 | preview.append(tr("Also sort the matching messages already in %1 (%n)", nullptr, count).arg(sieveNameFor(scope))); |
| 359 | else | ||
| 360 |
2/2✓ Branch 91 → 92 taken 13 times.
✓ Branch 95 → 96 taken 13 times.
|
26 | preview.append(tr("Existing matches in %1: %n (+now applies the rule to them)", nullptr, count) |
| 361 |
2/2✓ Branch 93 → 94 taken 13 times.
✓ Branch 94 → 95 taken 13 times.
|
26 | .arg(sieveNameFor(scope))); |
| 362 |
3/3✓ Branch 103 → 104 taken 15 times.
✓ Branch 106 → 107 taken 8 times.
✓ Branch 106 → 136 taken 7 times.
|
15 | if (evaluation.contains(QStringLiteral("shadowedBy"))) |
| 363 |
2/2✓ Branch 107 → 108 taken 8 times.
✓ Branch 122 → 123 taken 8 times.
|
16 | preview.append(tr("Rule „%1“ above catches these messages first and stops.") |
| 364 |
5/5✓ Branch 113 → 114 taken 8 times.
✓ Branch 114 → 115 taken 8 times.
✓ Branch 119 → 120 taken 8 times.
✓ Branch 120 → 121 taken 8 times.
✓ Branch 121 → 122 taken 8 times.
|
32 | .arg(evaluation.value(QStringLiteral("shadowedBy")).toMap().value(QStringLiteral("name")).toString())); |
| 365 |
2/2✓ Branch 147 → 138 taken 4 times.
✓ Branch 147 → 148 taken 15 times.
|
19 | for (const QString &target : parsed.forwardTargets) |
| 366 |
3/3✓ Branch 139 → 140 taken 4 times.
✓ Branch 141 → 142 taken 4 times.
✓ Branch 142 → 143 taken 4 times.
|
8 | preview.append(tr("Mails matching this rule will be sent to %1. They leave this mailbox.").arg(target)); |
| 367 |
2/3✓ Branch 149 → 150 taken 15 times.
✗ Branch 149 → 151 not taken.
✓ Branch 150 → 152 taken 15 times.
|
15 | const auto account = m_deps.account ? m_deps.account() : std::nullopt; |
| 368 |
6/7✓ Branch 152 → 153 taken 15 times.
✓ Branch 155 → 156 taken 15 times.
✗ Branch 155 → 158 not taken.
✓ Branch 159 → 160 taken 15 times.
✓ Branch 161 → 162 taken 15 times.
✓ Branch 162 → 163 taken 15 times.
✓ Branch 163 → 164 taken 15 times.
|
45 | preview.append(tr("Account: %1 · rule %2 of %2").arg(account ? account->email : QString()).arg(ruleCount() + 1)); |
| 369 |
3/5✓ Branch 173 → 174 taken 15 times.
✓ Branch 176 → 177 taken 15 times.
✓ Branch 176 → 178 taken 15 times.
✗ Branch 416 → 417 not taken.
✗ Branch 416 → 418 not taken.
|
45 | const QVariantList issues = draftIssues(-1, parsed.draft, {{QStringLiteral("forwardTargets"), parsed.forwardTargets}}); |
| 370 |
2/2✓ Branch 224 → 182 taken 1 time.
✓ Branch 224 → 225 taken 14 times.
|
15 | for (const QVariant &issue : issues) |
| 371 |
4/5✓ Branch 183 → 184 taken 1 time.
✓ Branch 188 → 189 taken 1 time.
✓ Branch 189 → 190 taken 1 time.
✓ Branch 195 → 196 taken 1 time.
✗ Branch 195 → 222 not taken.
|
2 | if (issue.toMap().value(QStringLiteral("blocking")).toBool()) { |
| 372 |
1/1✓ Branch 200 → 201 taken 1 time.
|
2 | result.insert(QStringLiteral("ok"), false); |
| 373 |
3/3✓ Branch 204 → 205 taken 1 time.
✓ Branch 209 → 210 taken 1 time.
✓ Branch 213 → 214 taken 1 time.
|
3 | result.insert(QStringLiteral("error"), issue.toMap().value(QStringLiteral("message"))); |
| 374 | 1 | break; | |
| 375 | } | ||
| 376 |
1/1✓ Branch 229 → 230 taken 15 times.
|
30 | result.insert(QStringLiteral("preview"), preview); |
| 377 |
1/1✓ Branch 237 → 238 taken 15 times.
|
30 | result.insert(QStringLiteral("draft"), parsed.draft); |
| 378 |
1/1✓ Branch 245 → 246 taken 15 times.
|
30 | result.insert(QStringLiteral("scopePath"), scope); |
| 379 | 15 | return result; | |
| 380 | 145 | } | |
| 381 | |||
| 382 | 5 | QVariantMap QuickRulesBackend::saveConsoleRule(const QString &text, bool confirmed) { | |
| 383 |
1/1✓ Branch 2 → 3 taken 5 times.
|
5 | const QVariantMap parsed = parseConsoleRule(text); |
| 384 |
4/4✓ Branch 7 → 8 taken 5 times.
✓ Branch 8 → 9 taken 5 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 34 taken 4 times.
|
10 | if (!parsed.value(QStringLiteral("ok")).toBool()) |
| 385 |
3/5✓ Branch 21 → 22 taken 1 time.
✓ Branch 25 → 26 taken 1 time.
✓ Branch 25 → 27 taken 1 time.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
|
4 | return {{QStringLiteral("error"), parsed.value(QStringLiteral("error"))}}; |
| 386 |
2/2✓ Branch 38 → 39 taken 4 times.
✓ Branch 39 → 40 taken 4 times.
|
8 | const QStringList forwards = parsed.value(QStringLiteral("forwardTargets")).toStringList(); |
| 387 |
6/6✓ Branch 45 → 46 taken 2 times.
✓ Branch 45 → 48 taken 2 times.
✓ Branch 46 → 47 taken 1 time.
✓ Branch 46 → 48 taken 1 time.
✓ Branch 49 → 50 taken 1 time.
✓ Branch 49 → 79 taken 3 times.
|
4 | if (!forwards.isEmpty() && !confirmed) |
| 388 | 2 | return {{QStringLiteral("needsConfirmation"), true}, | |
| 389 |
1/1✓ Branch 57 → 58 taken 1 time.
|
2 | {QStringLiteral("message"), tr("Mails matching this rule will be sent to %1. Press Enter again to confirm.") |
| 390 |
4/6✓ Branch 62 → 63 taken 1 time.
✓ Branch 63 → 64 taken 1 time.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 67 → 69 taken 1 time.
✗ Branch 213 → 214 not taken.
✗ Branch 213 → 215 not taken.
|
5 | .arg(forwards.join(QStringLiteral(", ")))}}; |
| 391 |
2/2✓ Branch 83 → 84 taken 3 times.
✓ Branch 84 → 85 taken 3 times.
|
6 | const QVariantMap draft = parsed.value(QStringLiteral("draft")).toMap(); |
| 392 |
2/4✓ Branch 101 → 102 taken 6 times.
✓ Branch 101 → 103 taken 3 times.
✗ Branch 266 → 267 not taken.
✗ Branch 266 → 268 not taken.
|
15 | const QString requestId = saveDraft(-1, draft, {{QStringLiteral("forwardTargets"), forwards}, |
| 393 |
1/1✓ Branch 98 → 99 taken 3 times.
|
9 | {QStringLiteral("everyMessage"), confirmed}}); |
| 394 |
4/4✓ Branch 111 → 112 taken 3 times.
✓ Branch 112 → 113 taken 3 times.
✓ Branch 117 → 118 taken 1 time.
✓ Branch 117 → 144 taken 2 times.
|
6 | if (parsed.value(QStringLiteral("applyNow")).toBool()) { |
| 395 |
2/2✓ Branch 122 → 123 taken 1 time.
✓ Branch 123 → 124 taken 1 time.
|
2 | const QString scope = parsed.value(QStringLiteral("scopePath")).toString(); |
| 396 |
1/1✓ Branch 128 → 129 taken 1 time.
|
1 | QPointer<QuickRulesBackend> guard(this); |
| 397 |
1/1✓ Branch 129 → 130 taken 1 time.
|
1 | auto connection = std::make_shared<QMetaObject::Connection>(); |
| 398 | 2 | *connection = connect(this, &QuickRulesBackend::editFinished, this, | |
| 399 |
1/9✓ Branch 135 → 136 taken 1 time.
✗ Branch 314 → 315 not taken.
✗ Branch 314 → 316 not taken.
✗ Branch 317 → 318 not taken.
✗ Branch 317 → 319 not taken.
✗ Branch 320 → 321 not taken.
✗ Branch 320 → 322 not taken.
✗ Branch 323 → 324 not taken.
✗ Branch 323 → 325 not taken.
|
2 | [this, guard, requestId, draft, scope, connection](const QString &id, const QVariantMap &result) { |
| 400 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
|
1 | if (id != requestId) |
| 401 | ✗ | return; | |
| 402 | 1 | disconnect(*connection); | |
| 403 |
9/24✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 17 not taken.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 17 not taken.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 20 not taken.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 22 not taken.
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 24 not taken.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 26 not taken.
✓ Branch 26 → 27 taken 1 time.
✗ Branch 26 → 30 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 37 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
|
2 | if (guard && result.value(QStringLiteral("ok")).toBool()) |
| 404 |
1/1✓ Branch 27 → 28 taken 1 time.
|
1 | applyToExisting(draft, scope); |
| 405 | 1 | }); | |
| 406 | 1 | } | |
| 407 |
2/4✓ Branch 150 → 151 taken 3 times.
✓ Branch 150 → 152 taken 3 times.
✗ Branch 336 → 337 not taken.
✗ Branch 336 → 338 not taken.
|
9 | return {{QStringLiteral("requestId"), requestId}}; |
| 408 | 26 | } | |
| 409 | |||
| 410 | // --- folders keep their rules (S95.4.7, SR-10) ---------------------------------------- | ||
| 411 | |||
| 412 | namespace { | ||
| 413 | |||
| 414 | 18 | bool targetsFolder(const QString &target, const QString &folder, const QString &delimiter) { | |
| 415 |
8/12✓ Branch 3 → 4 taken 14 times.
✓ Branch 3 → 9 taken 4 times.
✓ Branch 5 → 6 taken 14 times.
✗ Branch 5 → 10 not taken.
✓ Branch 6 → 7 taken 14 times.
✓ Branch 7 → 8 taken 14 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 14 times.
✓ Branch 12 → 13 taken 14 times.
✓ Branch 12 → 14 taken 4 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
|
18 | return target == folder || (!delimiter.isEmpty() && target.startsWith(folder + delimiter)); |
| 416 | } | ||
| 417 | |||
| 418 | } // namespace | ||
| 419 | |||
| 420 | 3 | void QuickRulesBackend::folderRenamed(const QString &oldImapPath, const QString &newImapPath) { | |
| 421 |
1/1✓ Branch 2 → 3 taken 3 times.
|
3 | SieveSession *s = session(); |
| 422 |
5/9✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 8 not taken.
✓ Branch 4 → 5 taken 3 times.
✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 3 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 3 times.
|
3 | if (!s || readOnly() || oldImapPath == newImapPath) |
| 423 | 1 | return; | |
| 424 |
2/3✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 15 not taken.
✓ Branch 14 → 16 taken 3 times.
|
3 | const QString oldName = m_deps.decodeMailbox ? m_deps.decodeMailbox(oldImapPath) : oldImapPath; |
| 425 |
2/3✓ Branch 17 → 18 taken 3 times.
✗ Branch 17 → 19 not taken.
✓ Branch 18 → 20 taken 3 times.
|
3 | const QString newName = m_deps.decodeMailbox ? m_deps.decodeMailbox(newImapPath) : newImapPath; |
| 426 |
2/2✓ Branch 20 → 21 taken 3 times.
✓ Branch 21 → 22 taken 3 times.
|
3 | const QString delimiter = mailboxDirectory().delimiter(); |
| 427 | 3 | Sieve::Document document = s->document(); | |
| 428 |
2/3✗ Branch 26 → 27 not taken.
✓ Branch 26 → 29 taken 3 times.
✓ Branch 30 → 31 taken 3 times.
|
3 | const int changed = Sieve::renameMailbox(&document, oldName, newName, delimiter.isEmpty() ? QLatin1Char('/') : delimiter.at(0)); |
| 429 |
7/7✓ Branch 31 → 32 taken 3 times.
✓ Branch 35 → 36 taken 3 times.
✓ Branch 36 → 37 taken 3 times.
✓ Branch 37 → 38 taken 3 times.
✓ Branch 38 → 39 taken 3 times.
✓ Branch 42 → 33 taken 3 times.
✓ Branch 42 → 43 taken 3 times.
|
6 | qCInfo(lcRulesBackend) << "Folder rename touches" << changed << "rule blocks"; |
| 430 |
2/2✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 45 taken 2 times.
|
3 | if (changed == 0) |
| 431 | 1 | return; | |
| 432 | 2 | submit({Sieve::encode(document), QStringLiteral("folder-rename"), | |
| 433 | tr("Rules updated for the renamed folder: %n", nullptr, changed), true}); | ||
| 434 | 10 | } | |
| 435 | |||
| 436 | 3 | QStringList QuickRulesBackend::rulesUsingFolder(const QString &imapPath) const { | |
| 437 | 3 | QStringList names; | |
| 438 |
1/1✓ Branch 2 → 3 taken 3 times.
|
3 | const SieveSession *s = session(); |
| 439 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
|
3 | if (!s) |
| 440 | ✗ | return names; | |
| 441 |
1/1✓ Branch 5 → 6 taken 3 times.
|
3 | const QString folder = sieveNameFor(imapPath); |
| 442 |
2/2✓ Branch 6 → 7 taken 3 times.
✓ Branch 7 → 8 taken 3 times.
|
3 | const QString delimiter = mailboxDirectory().delimiter(); |
| 443 | // S106-322: the script holds the name as a Sieve string — '\' and '"' | ||
| 444 | // escaped — so the advanced-block probe looks for that spelling. A hit | ||
| 445 | // inside another string only adds a warning line (safe side). | ||
| 446 | 6 | const auto sieveEscaped = [](QString text) { | |
| 447 |
3/3✓ Branch 5 → 6 taken 6 times.
✓ Branch 9 → 10 taken 6 times.
✓ Branch 10 → 11 taken 6 times.
|
6 | return text.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char('"'), QLatin1String("\\\"")).toUtf8(); |
| 448 | }; | ||
| 449 |
1/1✓ Branch 10 → 11 taken 3 times.
|
3 | const QByteArray quoted = sieveEscaped(folder); |
| 450 |
2/2✓ Branch 12 → 13 taken 3 times.
✓ Branch 13 → 14 taken 3 times.
|
3 | const QByteArray quotedPrefix = sieveEscaped(folder + delimiter); |
| 451 |
2/2✓ Branch 67 → 18 taken 14 times.
✓ Branch 67 → 68 taken 3 times.
|
17 | for (const Sieve::Block &block : s->document().blocks) { |
| 452 |
2/2✓ Branch 19 → 20 taken 9 times.
✓ Branch 19 → 39 taken 5 times.
|
14 | if (block.kind == Sieve::BlockKind::Rule) { |
| 453 |
2/2✓ Branch 37 → 22 taken 9 times.
✓ Branch 37 → 38 taken 7 times.
|
16 | for (const Sieve::Action &action : block.rule.actions) |
| 454 |
6/7✓ Branch 23 → 24 taken 9 times.
✗ Branch 23 → 27 not taken.
✓ Branch 24 → 25 taken 9 times.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 27 taken 7 times.
✓ Branch 28 → 29 taken 2 times.
✓ Branch 28 → 35 taken 7 times.
|
9 | if (action.kind == Sieve::Action::Kind::FileInto && targetsFolder(action.target, folder, delimiter)) { |
| 455 |
2/4✓ Branch 29 → 30 taken 2 times.
✗ Branch 29 → 31 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 32 → 33 taken 2 times.
|
2 | names.append(block.named ? block.name : RuleSentence::generatedName(block.rule)); |
| 456 | 2 | break; | |
| 457 | } | ||
| 458 |
4/4✓ Branch 39 → 40 taken 2 times.
✓ Branch 39 → 50 taken 3 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 49 taken 1 time.
|
8 | } else if (block.kind == Sieve::BlockKind::Advanced && |
| 459 |
12/19✓ Branch 40 → 41 taken 2 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 45 → 46 taken 1 time.
✓ Branch 48 → 49 taken 1 time.
✗ Branch 48 → 50 not taken.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 53 taken 4 times.
✓ Branch 53 → 54 taken 2 times.
✓ Branch 53 → 55 taken 3 times.
✓ Branch 55 → 56 taken 2 times.
✓ Branch 55 → 57 taken 3 times.
✓ Branch 57 → 58 taken 2 times.
✓ Branch 57 → 65 taken 3 times.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
✗ Branch 93 → 94 not taken.
✗ Branch 93 → 95 not taken.
✗ Branch 97 → 98 not taken.
✗ Branch 97 → 99 not taken.
|
8 | (block.original.contains("\"" + quoted + "\"") || block.original.contains("\"" + quotedPrefix))) { |
| 460 |
3/3✓ Branch 58 → 59 taken 2 times.
✓ Branch 60 → 61 taken 2 times.
✓ Branch 61 → 62 taken 2 times.
|
4 | names.append(tr("Advanced block (line %1)").arg(block.line)); |
| 461 | } | ||
| 462 | } | ||
| 463 | 3 | return names; | |
| 464 | 3 | } | |
| 465 | |||
| 466 | 3 | QString QuickRulesBackend::disableRulesForFolder(const QString &imapPath) { | |
| 467 |
1/1✓ Branch 2 → 3 taken 3 times.
|
3 | SieveSession *s = session(); |
| 468 |
4/7✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 14 taken 3 times.
|
3 | if (!s || readOnly()) |
| 469 | ✗ | return refuseEdit(tr("Rules can't be changed right now. Reload them and try again.")); | |
| 470 |
1/1✓ Branch 14 → 15 taken 3 times.
|
3 | const QString folder = sieveNameFor(imapPath); |
| 471 |
2/2✓ Branch 15 → 16 taken 3 times.
✓ Branch 16 → 17 taken 3 times.
|
3 | const QString delimiter = mailboxDirectory().delimiter(); |
| 472 | 3 | Sieve::Document document = s->document(); | |
| 473 | 3 | int changed = 0; | |
| 474 |
4/4✓ Branch 20 → 21 taken 3 times.
✓ Branch 21 → 22 taken 3 times.
✓ Branch 50 → 23 taken 13 times.
✓ Branch 50 → 51 taken 3 times.
|
16 | for (Sieve::Block &block : document.blocks) { |
| 475 |
3/4✓ Branch 24 → 25 taken 9 times.
✓ Branch 24 → 26 taken 4 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 9 times.
|
13 | if (block.kind != Sieve::BlockKind::Rule || !block.rule.enabled) |
| 476 | 4 | continue; | |
| 477 |
4/4✓ Branch 27 → 28 taken 9 times.
✓ Branch 28 → 29 taken 9 times.
✓ Branch 46 → 30 taken 9 times.
✓ Branch 46 → 47 taken 7 times.
|
16 | for (const Sieve::Action &action : block.rule.actions) |
| 478 |
6/7✓ Branch 31 → 32 taken 9 times.
✗ Branch 31 → 35 not taken.
✓ Branch 32 → 33 taken 9 times.
✓ Branch 33 → 34 taken 2 times.
✓ Branch 33 → 35 taken 7 times.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 36 → 44 taken 7 times.
|
9 | if (action.kind == Sieve::Action::Kind::FileInto && targetsFolder(action.target, folder, delimiter)) { |
| 479 | 2 | block.rule.enabled = false; | |
| 480 |
1/2✗ Branch 37 → 38 not taken.
✓ Branch 37 → 42 taken 2 times.
|
2 | if (!block.named) { |
| 481 | ✗ | block.name = RuleSentence::generatedName(block.rule); | |
| 482 | ✗ | block.named = true; | |
| 483 | } | ||
| 484 | 2 | block.rule.name = block.name; | |
| 485 | 2 | block.dirty = true; | |
| 486 | 2 | ++changed; | |
| 487 | 2 | break; | |
| 488 | } | ||
| 489 | } | ||
| 490 |
2/2✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 57 taken 2 times.
|
3 | if (changed == 0) |
| 491 |
2/2✓ Branch 52 → 53 taken 1 time.
✓ Branch 53 → 54 taken 1 time.
|
1 | return refuseEdit(tr("No rule files into this folder.")); |
| 492 | 2 | return submit({Sieve::encode(document), QStringLiteral("folder-delete"), | |
| 493 | 4 | tr("Rules turned off for the deleted folder: %n", nullptr, changed), true}); | |
| 494 | 7 | } | |
| 495 |