presentation/ComposeRecipients.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "ComposeRecipients.h" | ||
| 2 | #include "service/MimeParser.h" | ||
| 3 | #include "util/MailboxText.h" | ||
| 4 | #include "service/SmtpService.h" | ||
| 5 | #include "service/ImapResponseParser.h" | ||
| 6 | #include <QSet> | ||
| 7 | #include <algorithm> | ||
| 8 | #include <QUrl> | ||
| 9 | |||
| 10 | 8575 | std::optional<ComposeRecipients> ComposeRecipients::parse(const QString &field) { | |
| 11 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8575 times.
|
8575 | if (field.size() > 65536) return std::nullopt; |
| 12 | 8575 | ComposeRecipients result; | |
| 13 | 8575 | QString chunk; | |
| 14 | 8575 | bool quoted = false, escaped = false, angle = false; | |
| 15 | 8590 | const auto append = [&]() { | |
| 16 |
1/1✓ Branch 2 → 3 taken 8590 times.
|
8590 | const auto value = chunk.trimmed(); |
| 17 | 8590 | chunk.clear(); | |
| 18 |
2/2✓ Branch 5 → 6 taken 4003 times.
✓ Branch 5 → 7 taken 4587 times.
|
8590 | if (value.isEmpty()) return true; |
| 19 | 4587 | QString address = value; | |
| 20 | // S106-233: the angle address is the one outside a quoted display name — | ||
| 21 | // '"Boss <b@x>" <real@y>' names real@y, not b@x. | ||
| 22 | 4587 | qsizetype left = -1, right = -1; | |
| 23 | 4587 | bool inQuotes = false, escapedHere = false; | |
| 24 |
2/2✓ Branch 50 → 9 taken 98949 times.
✓ Branch 50 → 51 taken 4587 times.
|
103536 | for (qsizetype i = 0; i < value.size(); ++i) { |
| 25 | 98949 | const QChar c = value.at(i); | |
| 26 |
2/2✓ Branch 10 → 11 taken 3 times.
✓ Branch 10 → 12 taken 98946 times.
|
99099 | if (escapedHere) { escapedHere = false; continue; } |
| 27 |
6/6✓ Branch 12 → 13 taken 137 times.
✓ Branch 12 → 18 taken 98809 times.
✓ Branch 16 → 17 taken 3 times.
✓ Branch 16 → 18 taken 134 times.
✓ Branch 19 → 20 taken 3 times.
✓ Branch 19 → 21 taken 98943 times.
|
98946 | if (inQuotes && c == QLatin1Char('\\')) { escapedHere = true; continue; } |
| 28 |
2/2✓ Branch 24 → 25 taken 26 times.
✓ Branch 24 → 26 taken 98917 times.
|
98943 | if (c == QLatin1Char('"')) { inQuotes = !inQuotes; continue; } |
| 29 |
2/2✓ Branch 26 → 27 taken 121 times.
✓ Branch 26 → 28 taken 98796 times.
|
98917 | if (inQuotes) continue; |
| 30 |
5/6✓ Branch 31 → 32 taken 1138 times.
✓ Branch 31 → 34 taken 97658 times.
✓ Branch 32 → 33 taken 1138 times.
✗ Branch 32 → 34 not taken.
✓ Branch 35 → 36 taken 1138 times.
✓ Branch 35 → 37 taken 97658 times.
|
98796 | if (c == QLatin1Char('<') && left < 0) left = i; |
| 31 |
5/6✓ Branch 40 → 41 taken 1138 times.
✓ Branch 40 → 43 taken 96520 times.
✓ Branch 41 → 42 taken 1138 times.
✗ Branch 41 → 43 not taken.
✓ Branch 44 → 45 taken 1138 times.
✓ Branch 44 → 46 taken 96520 times.
|
97658 | else if (c == QLatin1Char('>') && right < 0) right = i; |
| 32 | } | ||
| 33 |
2/2✓ Branch 51 → 52 taken 1138 times.
✓ Branch 51 → 71 taken 3449 times.
|
4587 | if (left >= 0) { |
| 34 |
9/16✓ Branch 52 → 53 taken 1138 times.
✗ Branch 52 → 57 not taken.
✓ Branch 53 → 54 taken 1138 times.
✓ Branch 54 → 55 taken 1138 times.
✓ Branch 56 → 57 taken 1 time.
✓ Branch 56 → 58 taken 1137 times.
✓ Branch 59 → 60 taken 1138 times.
✗ Branch 59 → 61 not taken.
✓ Branch 61 → 62 taken 1138 times.
✗ Branch 61 → 63 not taken.
✓ Branch 63 → 64 taken 1 time.
✓ Branch 63 → 65 taken 1137 times.
✗ Branch 140 → 141 not taken.
✗ Branch 140 → 142 not taken.
✗ Branch 144 → 145 not taken.
✗ Branch 144 → 146 not taken.
|
1138 | if (right <= left || !value.mid(right + 1).trimmed().isEmpty()) return false; |
| 35 |
2/2✓ Branch 65 → 66 taken 1137 times.
✓ Branch 66 → 67 taken 1137 times.
|
1137 | address = value.mid(left + 1, right - left - 1).trimmed(); |
| 36 |
1/2✗ Branch 71 → 72 not taken.
✓ Branch 71 → 73 taken 3449 times.
|
3449 | } else if (right >= 0) return false; |
| 37 | // F-21 (SPRINT-104): an internationalized domain (name@bücher.de) is | ||
| 38 | // valid mail — the envelope carries its Punycode ACE form (RFC 5891), | ||
| 39 | // the header keeps what the user typed. | ||
| 40 | 4586 | QString envelope = address; | |
| 41 | 4586 | const auto at = address.lastIndexOf(QLatin1Char('@')); | |
| 42 |
1/1✓ Branch 77 → 78 taken 4586 times.
|
4586 | const auto domain = address.mid(at + 1); |
| 43 |
2/3✓ Branch 80 → 81 taken 4586 times.
✓ Branch 81 → 82 taken 4586 times.
✗ Branch 81 → 115 not taken.
|
4586 | if (!domain.startsWith(QLatin1Char('['))) { |
| 44 | 4586 | bool nonAscii = false; | |
| 45 |
2/2✓ Branch 88 → 84 taken 55644 times.
✓ Branch 88 → 89 taken 4583 times.
|
60227 | for (const auto &ch : domain) |
| 46 |
2/2✓ Branch 85 → 86 taken 3 times.
✓ Branch 85 → 87 taken 55641 times.
|
55644 | if (ch.unicode() > 127) { nonAscii = true; break; } |
| 47 |
2/2✓ Branch 89 → 90 taken 3 times.
✓ Branch 89 → 115 taken 4583 times.
|
4586 | if (nonAscii) { |
| 48 |
2/2✓ Branch 91 → 92 taken 3 times.
✓ Branch 92 → 93 taken 3 times.
|
3 | const QString ace = QUrl::toAce(domain); |
| 49 |
6/7✓ Branch 95 → 96 taken 2 times.
✓ Branch 95 → 100 taken 1 time.
✓ Branch 98 → 99 taken 2 times.
✗ Branch 99 → 100 not taken.
✓ Branch 99 → 101 taken 2 times.
✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 104 taken 2 times.
|
3 | if (ace.isEmpty() || ace.contains(QLatin1Char(' '))) return false; |
| 50 |
2/2✓ Branch 104 → 105 taken 2 times.
✓ Branch 105 → 106 taken 2 times.
|
2 | envelope = address.left(at + 1) + ace; |
| 51 | 3 | } | |
| 52 | } | ||
| 53 |
1/1✓ Branch 115 → 116 taken 4585 times.
|
4585 | if (!SmtpService::isValidSmtpAddress(envelope) || |
| 54 |
10/12✓ Branch 116 → 117 taken 4571 times.
✓ Branch 116 → 127 taken 14 times.
✓ Branch 119 → 120 taken 4571 times.
✓ Branch 120 → 121 taken 3 times.
✓ Branch 120 → 125 taken 4568 times.
✓ Branch 123 → 124 taken 3 times.
✗ Branch 124 → 125 not taken.
✓ Branch 124 → 127 taken 3 times.
✗ Branch 126 → 127 not taken.
✓ Branch 126 → 128 taken 4568 times.
✓ Branch 129 → 130 taken 17 times.
✓ Branch 129 → 131 taken 4568 times.
|
9153 | (!domain.contains(QLatin1Char('.')) && !domain.startsWith(QLatin1Char('['))) || |
| 55 | 4585 | result.envelope.size() >= 1000) return false; | |
| 56 |
1/1✓ Branch 131 → 132 taken 4568 times.
|
4568 | result.headers.append(value); |
| 57 |
1/1✓ Branch 132 → 133 taken 4568 times.
|
4568 | result.envelope.append(envelope); |
| 58 | 4568 | return true; | |
| 59 | 8590 | }; | |
| 60 |
2/2✓ Branch 66 → 7 taken 99082 times.
✓ Branch 66 → 67 taken 8574 times.
|
107656 | for (const auto ch : field) { |
| 61 |
5/6✓ Branch 8 → 9 taken 99081 times.
✓ Branch 8 → 11 taken 1 time.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 99081 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 99081 times.
|
99082 | if (ch.unicode() < 0x20 || ch.unicode() == 0x7f) return std::nullopt; |
| 62 |
3/3✓ Branch 15 → 16 taken 3 times.
✓ Branch 15 → 18 taken 99078 times.
✓ Branch 16 → 17 taken 3 times.
|
99081 | if (escaped) { escaped = false; chunk.append(ch); continue; } |
| 63 |
7/7✓ Branch 18 → 19 taken 168 times.
✓ Branch 18 → 24 taken 98910 times.
✓ Branch 22 → 23 taken 3 times.
✓ Branch 22 → 24 taken 165 times.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 25 → 28 taken 99075 times.
✓ Branch 26 → 27 taken 3 times.
|
99078 | if (quoted && ch == QLatin1Char('\\')) { escaped = true; chunk.append(ch); continue; } |
| 64 |
2/2✓ Branch 31 → 32 taken 27 times.
✓ Branch 31 → 33 taken 99048 times.
|
99075 | if (ch == QLatin1Char('"')) quoted = !quoted; |
| 65 |
2/2✓ Branch 33 → 34 taken 98909 times.
✓ Branch 33 → 64 taken 166 times.
|
99075 | if (!quoted) { |
| 66 |
2/2✓ Branch 37 → 38 taken 1138 times.
✓ Branch 37 → 41 taken 97771 times.
|
98909 | if (ch == QLatin1Char('<')) { |
| 67 |
1/2✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 1138 times.
|
1138 | if (angle) return std::nullopt; |
| 68 | 1138 | angle = true; | |
| 69 |
2/2✓ Branch 44 → 45 taken 1138 times.
✓ Branch 44 → 48 taken 96633 times.
|
97771 | } else if (ch == QLatin1Char('>')) { |
| 70 |
1/2✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 1138 times.
|
1138 | if (!angle) return std::nullopt; |
| 71 | 1138 | angle = false; | |
| 72 |
7/8✓ Branch 48 → 49 taken 75120 times.
✓ Branch 48 → 58 taken 21513 times.
✓ Branch 52 → 53 taken 75103 times.
✓ Branch 52 → 57 taken 17 times.
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 58 taken 75103 times.
✓ Branch 59 → 60 taken 17 times.
✓ Branch 59 → 64 taken 96616 times.
|
96633 | } else if (!angle && (ch == QLatin1Char(',') || ch == QLatin1Char(';'))) { |
| 73 |
2/3✓ Branch 60 → 61 taken 17 times.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 63 taken 17 times.
|
17 | if (!append()) return std::nullopt; |
| 74 | 17 | continue; | |
| 75 | } | ||
| 76 | } | ||
| 77 |
1/1✓ Branch 64 → 65 taken 99058 times.
|
99058 | chunk.append(ch); |
| 78 | } | ||
| 79 |
9/11✓ Branch 67 → 69 taken 8573 times.
✓ Branch 67 → 73 taken 1 time.
✓ Branch 69 → 70 taken 8573 times.
✗ Branch 69 → 73 not taken.
✓ Branch 70 → 71 taken 8573 times.
✗ Branch 70 → 73 not taken.
✓ Branch 71 → 72 taken 8573 times.
✓ Branch 72 → 73 taken 19 times.
✓ Branch 72 → 74 taken 8554 times.
✓ Branch 75 → 76 taken 20 times.
✓ Branch 75 → 77 taken 8554 times.
|
8574 | if (quoted || escaped || angle || !append()) return std::nullopt; |
| 80 | 8554 | return result; | |
| 81 | 8575 | } | |
| 82 | ✗ | QStringList ComposeRecipients::combinedEnvelope(const ComposeRecipients &to, | |
| 83 | const ComposeRecipients &cc, const ComposeRecipients &bcc) { | ||
| 84 | ✗ | QStringList result; | |
| 85 | ✗ | QSet<QString> seen; | |
| 86 | ✗ | for (const auto *field : {&to, &cc, &bcc}) { | |
| 87 | ✗ | for (const auto &address : field->envelope) { | |
| 88 | ✗ | const auto key = addressKey(address); | |
| 89 | ✗ | if (seen.contains(key)) continue; | |
| 90 | ✗ | seen.insert(key); | |
| 91 | ✗ | result.append(address); | |
| 92 | ✗ | } | |
| 93 | } | ||
| 94 | ✗ | return result; | |
| 95 | ✗ | } | |
| 96 | |||
| 97 | 1068 | QString ComposeRecipients::mailbox(const QString &name, const QString &address) { | |
| 98 | 1068 | return MailboxText::mailbox(name, address); | |
| 99 | } | ||
| 100 | |||
| 101 | 174 | QString ComposeRecipients::addressKey(const QString &address) { | |
| 102 | 174 | const auto at = address.lastIndexOf(QLatin1Char('@')); | |
| 103 |
5/5✓ Branch 5 → 6 taken 174 times.
✓ Branch 6 → 7 taken 174 times.
✓ Branch 9 → 10 taken 174 times.
✓ Branch 10 → 11 taken 174 times.
✓ Branch 11 → 12 taken 174 times.
|
174 | return address.left(at) + QLatin1Char('@') + address.mid(at + 1).toLower(); |
| 104 | } | ||
| 105 | namespace { | ||
| 106 | |||
| 107 | // One mailbox of a received header as the composer shows it: an encoded-word | ||
| 108 | // name decoded, unless the decoded name holds angle brackets or control | ||
| 109 | // characters (S106-233) — even quoted, '<' '>' read as an address to other | ||
| 110 | // programs, and a control character makes the whole field unusable. | ||
| 111 | 48 | QString displayEntry(QString display, const QString &address) { | |
| 112 | 48 | const auto left = display.lastIndexOf(QLatin1Char('<')); | |
| 113 |
13/17✓ Branch 5 → 6 taken 32 times.
✓ Branch 5 → 10 taken 16 times.
✓ Branch 6 → 7 taken 32 times.
✓ Branch 7 → 8 taken 32 times.
✓ Branch 8 → 9 taken 32 times.
✓ Branch 9 → 10 taken 28 times.
✓ Branch 9 → 11 taken 4 times.
✓ Branch 12 → 13 taken 32 times.
✓ Branch 12 → 14 taken 16 times.
✓ Branch 14 → 15 taken 32 times.
✓ Branch 14 → 16 taken 16 times.
✓ Branch 16 → 17 taken 44 times.
✓ Branch 16 → 18 taken 4 times.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 52 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
|
48 | if (left <= 0 || !display.left(left).contains("=?")) return display; |
| 114 |
3/3✓ Branch 18 → 19 taken 4 times.
✓ Branch 19 → 20 taken 4 times.
✓ Branch 20 → 21 taken 4 times.
|
4 | auto name = ImapResponseParser::decodeRfc2047(display.left(left).trimmed()); |
| 115 |
1/1✓ Branch 25 → 26 taken 4 times.
|
4 | const bool plain = std::none_of(name.cbegin(), name.cend(), [](QChar c) { |
| 116 |
5/8✓ Branch 3 → 4 taken 22 times.
✗ Branch 3 → 14 not taken.
✓ Branch 5 → 6 taken 22 times.
✗ Branch 5 → 14 not taken.
✓ Branch 9 → 10 taken 21 times.
✓ Branch 9 → 14 taken 1 time.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 21 times.
|
22 | return c.unicode() < 0x20 || c.unicode() == 0x7f || c == QLatin1Char('<') || c == QLatin1Char('>'); |
| 117 | }); | ||
| 118 |
2/2✓ Branch 26 → 27 taken 1 time.
✓ Branch 26 → 28 taken 3 times.
|
4 | if (!plain) return display; |
| 119 |
3/3✓ Branch 28 → 29 taken 3 times.
✓ Branch 29 → 30 taken 3 times.
✓ Branch 30 → 31 taken 3 times.
|
3 | name.replace("\\", "\\\\"); |
| 120 |
3/3✓ Branch 33 → 34 taken 3 times.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 35 → 36 taken 3 times.
|
3 | name.replace("\"", "\\\""); |
| 121 |
4/4✓ Branch 38 → 39 taken 3 times.
✓ Branch 39 → 40 taken 3 times.
✓ Branch 40 → 41 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
|
3 | return "\"" + name + "\" <" + address + ">"; |
| 122 | 4 | } | |
| 123 | |||
| 124 | } // namespace | ||
| 125 | |||
| 126 | 10 | QString ComposeRecipients::decodedField(const QString &field) { | |
| 127 |
1/1✓ Branch 2 → 3 taken 10 times.
|
10 | const auto parsed = parse(field); |
| 128 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 10 times.
|
10 | if (!parsed) return field; |
| 129 | 10 | QStringList entries; | |
| 130 |
4/4✓ Branch 12 → 13 taken 6 times.
✓ Branch 13 → 14 taken 6 times.
✓ Branch 19 → 7 taken 6 times.
✓ Branch 19 → 20 taken 10 times.
|
16 | for (int i = 0; i < parsed->envelope.size(); ++i) entries.append(displayEntry(parsed->headers.at(i), parsed->envelope.at(i))); |
| 131 |
1/1✓ Branch 23 → 24 taken 10 times.
|
10 | return entries.join(QStringLiteral(", ")); |
| 132 | 10 | } | |
| 133 | |||
| 134 | 2 | QStringList ComposeRecipients::sentHeaders(const QByteArray &message, const QStringList &bcc) { | |
| 135 | 2 | QStringList headers; | |
| 136 |
1/1✓ Branch 2 → 3 taken 2 times.
|
2 | const auto fields = MimeParser::messageHeaders(message); |
| 137 |
2/2✓ Branch 26 → 13 taken 4 times.
✓ Branch 26 → 27 taken 2 times.
|
14 | for (const QString &name : {QStringLiteral("to"), QStringLiteral("cc")}) |
| 138 |
7/10✓ Branch 14 → 15 taken 4 times.
✓ Branch 15 → 16 taken 4 times.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 24 not taken.
✓ Branch 23 → 24 taken 4 times.
✓ Branch 28 → 29 taken 4 times.
✓ Branch 28 → 32 taken 2 times.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 65 not taken.
|
10 | if (const auto parsed = parse(decodedField(fields.value(name)))) headers += parsed->headers; |
| 139 |
2/2✓ Branch 44 → 35 taken 1 time.
✓ Branch 44 → 45 taken 2 times.
|
3 | for (const QString &mailbox : bcc) |
| 140 |
3/4✓ Branch 36 → 37 taken 1 time.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 41 not taken.
✓ Branch 40 → 41 taken 1 time.
|
1 | if (const auto parsed = parse(mailbox)) headers += parsed->headers; |
| 141 | 2 | return headers; | |
| 142 | 2 | } | |
| 143 | |||
| 144 | 39 | ComposeReplyRecipients ComposeRecipients::replyTargets(const QString &from, const QString &to, | |
| 145 | const QString &cc, const QString &replyTo, const QString &ownAddress, bool all) { | ||
| 146 |
1/1✓ Branch 2 → 3 taken 39 times.
|
39 | const auto own = addressKey(ownAddress); |
| 147 |
1/1✓ Branch 3 → 4 taken 39 times.
|
39 | const auto fromField = parse(from); |
| 148 | 39 | bool ownSender = false; | |
| 149 |
1/2✓ Branch 5 → 6 taken 39 times.
✗ Branch 5 → 22 not taken.
|
39 | if (fromField) { |
| 150 |
2/2✓ Branch 20 → 9 taken 39 times.
✓ Branch 20 → 21 taken 39 times.
|
78 | for (const auto &address : fromField->envelope) |
| 151 |
5/9✓ Branch 10 → 11 taken 39 times.
✗ Branch 10 → 14 not taken.
✓ Branch 11 → 12 taken 39 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 37 times.
✓ Branch 16 → 17 taken 39 times.
✗ Branch 16 → 18 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
|
39 | ownSender = ownSender || addressKey(address) == own; |
| 152 | } | ||
| 153 |
2/4✓ Branch 25 → 26 taken 39 times.
✓ Branch 25 → 27 taken 39 times.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
|
78 | QSet<QString> seen{own}; |
| 154 | 39 | QStringList targets, copies; | |
| 155 | 50 | const auto add = [&](const QString &field, QStringList &destination) { | |
| 156 |
1/1✓ Branch 2 → 3 taken 50 times.
|
50 | const auto parsed = parse(field); |
| 157 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 50 times.
|
50 | if (!parsed) return; |
| 158 |
2/2✓ Branch 32 → 7 taken 53 times.
✓ Branch 32 → 33 taken 50 times.
|
103 | for (int i = 0; i < parsed->envelope.size(); ++i) { |
| 159 |
1/1✓ Branch 9 → 10 taken 53 times.
|
53 | const auto key = addressKey(parsed->envelope.at(i)); |
| 160 |
2/2✓ Branch 11 → 12 taken 11 times.
✓ Branch 11 → 13 taken 42 times.
|
53 | if (seen.contains(key)) continue; |
| 161 |
1/1✓ Branch 13 → 14 taken 42 times.
|
42 | seen.insert(key); |
| 162 |
1/1✓ Branch 19 → 20 taken 42 times.
|
42 | const QString display = displayEntry(parsed->headers.at(i), parsed->envelope.at(i)); |
| 163 |
1/1✓ Branch 21 → 22 taken 42 times.
|
42 | destination.append(display); |
| 164 | 53 | } | |
| 165 |
1/2✓ Branch 35 → 36 taken 50 times.
✗ Branch 35 → 38 not taken.
|
50 | }; |
| 166 |
8/10✓ Branch 27 → 28 taken 2 times.
✓ Branch 27 → 29 taken 37 times.
✓ Branch 29 → 30 taken 37 times.
✓ Branch 31 → 32 taken 35 times.
✓ Branch 31 → 33 taken 2 times.
✓ Branch 35 → 36 taken 39 times.
✓ Branch 36 → 37 taken 37 times.
✓ Branch 36 → 38 taken 2 times.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
|
39 | add(ownSender ? to : replyTo.trimmed().isEmpty() ? from : replyTo, targets); |
| 167 |
2/2✓ Branch 38 → 39 taken 6 times.
✓ Branch 38 → 42 taken 33 times.
|
39 | if (all) { |
| 168 |
3/3✓ Branch 39 → 40 taken 5 times.
✓ Branch 39 → 41 taken 1 time.
✓ Branch 40 → 41 taken 5 times.
|
6 | if (!ownSender) add(to, targets); |
| 169 |
1/1✓ Branch 41 → 42 taken 6 times.
|
6 | add(cc, copies); |
| 170 | } | ||
| 171 |
1/1✓ Branch 42 → 43 taken 39 times.
|
117 | return {targets.join(", "), copies.join(", ")}; |
| 172 | 117 | } | |
| 173 |