presentation/QuickInvitationMail.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "QuickInvitation.h" | ||
| 2 | #include "ComposeRecipients.h" | ||
| 3 | #include "app/core/MailSessionCore.h" | ||
| 4 | #include "data/ICalendar.h" | ||
| 5 | #include "data/ImipInvitation.h" | ||
| 6 | #include "data/MailCache.h" | ||
| 7 | #include "service/MailPayloadExportService.h" | ||
| 8 | #include "service/MimeParser.h" | ||
| 9 | #include <QLoggingCategory> | ||
| 10 | |||
| 11 | Q_DECLARE_LOGGING_CATEGORY(lcInvitation) | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | // iMIP parts are small; a bound keeps a hostile attachment out of memory. | ||
| 15 | constexpr qint64 kMaxCalendarBytes = 1024 * 1024; | ||
| 16 | |||
| 17 | 1461 | QString singleSender(const QString &header) { | |
| 18 |
1/1✓ Branch 2 → 3 taken 1461 times.
|
1461 | const auto parsed = ComposeRecipients::parse(header); |
| 19 |
4/5✓ Branch 4 → 5 taken 1461 times.
✗ Branch 4 → 12 not taken.
✓ Branch 7 → 8 taken 1036 times.
✓ Branch 7 → 12 taken 425 times.
✓ Branch 10 → 11 taken 1036 times.
|
2922 | return parsed && parsed->envelope.size() == 1 ? Imip::addressOf(parsed->envelope.first()) : QString(); |
| 20 | 1461 | } | |
| 21 | |||
| 22 | 25 | bool isCalendarAttachment(const QVariantMap &attachment) { | |
| 23 |
3/3✓ Branch 6 → 7 taken 25 times.
✓ Branch 7 → 8 taken 25 times.
✓ Branch 8 → 9 taken 25 times.
|
50 | const QString type = attachment.value(QStringLiteral("contentType")).toString().toLower(); |
| 24 |
3/3✓ Branch 18 → 19 taken 25 times.
✓ Branch 19 → 20 taken 25 times.
✓ Branch 20 → 21 taken 25 times.
|
50 | const QString name = attachment.value(QStringLiteral("name")).toString().toLower(); |
| 25 |
4/5✓ Branch 27 → 28 taken 25 times.
✓ Branch 28 → 29 taken 21 times.
✓ Branch 28 → 35 taken 4 times.
✓ Branch 31 → 32 taken 21 times.
✗ Branch 31 → 35 not taken.
|
46 | return type.startsWith(QLatin1String("text/calendar")) || type == QLatin1String("application/ics") || |
| 26 |
2/3✓ Branch 33 → 34 taken 21 times.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 21 times.
|
46 | name.endsWith(QLatin1String(".ics")); |
| 27 | 25 | } | |
| 28 | |||
| 29 | } // namespace | ||
| 30 | |||
| 31 | 6851 | void QuickInvitation::setMail(const MailId &mail, const QVariantList &attachments, const QString &from) { | |
| 32 | 6851 | qint64 attachmentId = 0; | |
| 33 |
2/2✓ Branch 25 → 4 taken 25 times.
✓ Branch 25 → 26 taken 6847 times.
|
6872 | for (const QVariant &value : attachments) { |
| 34 |
1/1✓ Branch 5 → 6 taken 25 times.
|
25 | const QVariantMap attachment = value.toMap(); |
| 35 |
3/3✓ Branch 6 → 7 taken 25 times.
✓ Branch 7 → 8 taken 21 times.
✓ Branch 7 → 9 taken 4 times.
|
25 | if (!isCalendarAttachment(attachment)) continue; |
| 36 |
2/2✓ Branch 13 → 14 taken 4 times.
✓ Branch 14 → 15 taken 4 times.
|
8 | attachmentId = attachment.value(QStringLiteral("id")).toLongLong(); |
| 37 | 4 | break; | |
| 38 | 25 | } | |
| 39 |
8/8✓ Branch 27 → 28 taken 5452 times.
✓ Branch 27 → 32 taken 1399 times.
✓ Branch 28 → 29 taken 5449 times.
✓ Branch 28 → 32 taken 3 times.
✓ Branch 30 → 31 taken 5391 times.
✓ Branch 30 → 32 taken 58 times.
✓ Branch 33 → 34 taken 5391 times.
✓ Branch 33 → 35 taken 1460 times.
|
8308 | if (mail == m_mail && attachmentId == m_attachmentId && from == m_mailFrom) return; |
| 40 | 1460 | m_mail = mail; | |
| 41 | 1460 | m_mailFrom = from; | |
| 42 |
1/1✓ Branch 37 → 38 taken 1460 times.
|
1460 | m_mailSender = singleSender(from); |
| 43 | 1460 | m_attachmentId = attachmentId; | |
| 44 | 1460 | const quint64 generation = ++m_readGeneration; | |
| 45 |
2/2✓ Branch 40 → 41 taken 1460 times.
✓ Branch 41 → 42 taken 1460 times.
|
1460 | MailPayloadExportService::shared()->cancel(this); |
| 46 |
3/3✓ Branch 43 → 44 taken 4 times.
✓ Branch 43 → 45 taken 1456 times.
✓ Branch 44 → 45 taken 4 times.
|
1460 | if (m_invitation) clear(); |
| 47 | 1460 | auto *cache = m_session.cache(); | |
| 48 |
7/8✓ Branch 47 → 48 taken 1226 times.
✓ Branch 47 → 50 taken 234 times.
✓ Branch 48 → 49 taken 3 times.
✓ Branch 48 → 50 taken 1223 times.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 51 taken 3 times.
✓ Branch 52 → 53 taken 1457 times.
✓ Branch 52 → 54 taken 3 times.
|
1460 | if (!mail.isValid() || attachmentId <= 0 || !cache) return; |
| 49 | 9 | MailPayloadRequest request{cache->databasePath(), mail, cache->uidValidity(mail.folderId), | |
| 50 |
1/5✓ Branch 56 → 57 taken 3 times.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ Branch 93 → 94 not taken.
✗ Branch 93 → 95 not taken.
|
3 | MailPayloadRequest::Kind::Attachment, attachmentId, 0, kMaxCalendarBytes}; |
| 51 | // S106-343: the header block of a real invitation can pass 16 KiB (long | ||
| 52 | // Received chains, DKIM/ARC sets); a cut block made the sender "unknown". | ||
| 53 | 3 | constexpr qint64 kHeaderWindowBytes = 256 * 1024; | |
| 54 | 9 | const MailPayloadRequest headers{cache->databasePath(), mail, cache->uidValidity(mail.folderId), | |
| 55 |
1/5✓ Branch 59 → 60 taken 3 times.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
|
3 | MailPayloadRequest::Kind::RawSource, 0, 0, kHeaderWindowBytes}; |
| 56 |
3/3✓ Branch 60 → 61 taken 3 times.
✓ Branch 62 → 63 taken 3 times.
✓ Branch 64 → 65 taken 3 times.
|
6 | MailPayloadExportService::shared()->readWindow(this, headers, |
| 57 | 6 | [this, generation, request](QByteArray prefix, MailPayloadReadResult headerResult) { | |
| 58 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3 times.
|
3 | if (generation != m_readGeneration) return; |
| 59 |
3/4✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 10 taken 1 time.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 2 times.
|
3 | const bool completeHeader = prefix.contains("\r\n\r\n") || prefix.contains("\n\n"); |
| 60 | 3 | m_mailSender.clear(); | |
| 61 |
3/4✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 48 taken 2 times.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 48 not taken.
|
3 | if (headerResult.completed && completeHeader) { |
| 62 |
1/1✓ Branch 15 → 16 taken 1 time.
|
1 | const auto fields = MimeParser::messageHeaders(prefix); |
| 63 |
2/2✓ Branch 19 → 20 taken 1 time.
✓ Branch 30 → 31 taken 1 time.
|
4 | m_mailSender = singleSender(fields.contains(QStringLiteral("sender")) |
| 64 |
7/24✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 26 not taken.
✓ Branch 25 → 30 taken 1 time.
✗ Branch 29 → 30 not taken.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 1 time.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 1 time.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 40 not taken.
✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 42 not taken.
✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 44 not taken.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 60 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 66 → 67 not taken.
✗ Branch 66 → 68 not taken.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 76 not taken.
|
4 | ? fields.value(QStringLiteral("sender")) : fields.value(QStringLiteral("from"), m_mailFrom)); |
| 65 | 1 | } | |
| 66 |
1/1✓ Branch 51 → 52 taken 3 times.
|
6 | MailPayloadExportService::shared()->readWindow(this, request, |
| 67 | 6 | [this, generation](QByteArray bytes, MailPayloadReadResult result) { | |
| 68 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3 times.
|
3 | if (generation != m_readGeneration) return; |
| 69 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 17 taken 3 times.
|
3 | if (!result.completed) { |
| 70 | ✗ | qCWarning(lcInvitation) << "Calendar attachment unreadable:" << result.error; | |
| 71 | ✗ | return; | |
| 72 | } | ||
| 73 |
2/2✓ Branch 17 → 18 taken 3 times.
✓ Branch 18 → 19 taken 3 times.
|
3 | showCalendarText(QString::fromUtf8(bytes)); |
| 74 | }); | ||
| 75 | }); | ||
| 76 | 3 | } | |
| 77 | |||
| 78 | // S108.8.7 (D-24, client mode): an answer to a meeting the user organizes | ||
| 79 | // updates the attendee's state without a click — only when the stored | ||
| 80 | // event is organized by one of the user's addresses, the mail's sender is | ||
| 81 | // the replier (trust "ok"), the answer is not older than the stored | ||
| 82 | // SEQUENCE and MailJD (not the server) delivers the meeting. With server | ||
| 83 | // scheduling the server already recorded it (the card says so). | ||
| 84 | 3 | void QuickInvitation::applyReplyAutomatically() { | |
| 85 |
1/1✓ Branch 2 → 3 taken 3 times.
|
3 | const auto stored = storedCopy(); |
| 86 |
11/26✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 21 not taken.
✓ Branch 10 → 11 taken 3 times.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 21 taken 1 time.
✓ Branch 33 → 34 taken 3 times.
✗ Branch 33 → 35 not taken.
✓ Branch 35 → 36 taken 3 times.
✗ Branch 35 → 37 not taken.
✓ Branch 37 → 38 taken 3 times.
✗ Branch 37 → 39 not taken.
✓ Branch 39 → 40 taken 3 times.
✗ Branch 39 → 41 not taken.
✓ Branch 41 → 42 taken 1 time.
✓ Branch 41 → 43 taken 2 times.
✗ Branch 121 → 122 not taken.
✗ Branch 121 → 123 not taken.
✗ Branch 125 → 126 not taken.
✗ Branch 125 → 127 not taken.
✗ Branch 129 → 130 not taken.
✗ Branch 129 → 131 not taken.
✗ Branch 133 → 134 not taken.
✗ Branch 133 → 135 not taken.
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
|
11 | if (!stored || m_card.value(QStringLiteral("trust")).toString() != QLatin1String("ok") || |
| 87 |
12/22✓ Branch 18 → 19 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 2 times.
✓ Branch 23 → 24 taken 2 times.
✓ Branch 23 → 25 taken 1 time.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 25 → 27 taken 1 time.
✓ Branch 27 → 28 taken 2 times.
✓ Branch 27 → 29 taken 1 time.
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 31 taken 1 time.
✓ Branch 31 → 32 taken 3 times.
✗ Branch 31 → 33 not taken.
✗ Branch 105 → 106 not taken.
✗ Branch 105 → 107 not taken.
✗ Branch 109 → 110 not taken.
✗ Branch 109 → 111 not taken.
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 115 not taken.
✗ Branch 117 → 118 not taken.
✗ Branch 117 → 119 not taken.
|
7 | m_card.value(QStringLiteral("replyApplied")).toBool()) |
| 88 | 1 | return; | |
| 89 | const QString organizer = | ||
| 90 |
3/3✓ Branch 47 → 48 taken 2 times.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 52 → 53 taken 2 times.
|
4 | Imip::addressOf(ICal::parseFirst(stored->master.icalRaw, QStringLiteral("VEVENT")).value(QStringLiteral("ORGANIZER"))); |
| 91 |
5/11✓ Branch 60 → 61 taken 2 times.
✗ Branch 60 → 64 not taken.
✓ Branch 61 → 62 taken 2 times.
✗ Branch 63 → 64 not taken.
✓ Branch 63 → 65 taken 2 times.
✓ Branch 66 → 67 taken 2 times.
✗ Branch 66 → 68 not taken.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 70 taken 2 times.
✗ Branch 160 → 161 not taken.
✗ Branch 160 → 162 not taken.
|
2 | if (organizer.isEmpty() || !ownAddresses().contains(organizer)) return; |
| 92 |
2/3✓ Branch 71 → 72 taken 2 times.
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 2 times.
|
2 | if (serverSends(stored->accountId)) return; |
| 93 |
3/3✓ Branch 76 → 77 taken 2 times.
✓ Branch 77 → 78 taken 1 time.
✓ Branch 77 → 91 taken 1 time.
|
2 | if (m_invitation->sequence < Imip::sequenceOf(stored->master.icalRaw)) { |
| 94 |
6/6✓ Branch 78 → 79 taken 1 time.
✓ Branch 82 → 83 taken 1 time.
✓ Branch 83 → 84 taken 1 time.
✓ Branch 85 → 86 taken 1 time.
✓ Branch 89 → 80 taken 1 time.
✓ Branch 89 → 90 taken 1 time.
|
2 | qCInfo(lcInvitation) << "Reply older than the stored meeting; not applied" << m_invitation->uid; |
| 95 | 1 | return; | |
| 96 | } | ||
| 97 |
1/1✓ Branch 91 → 92 taken 1 time.
|
1 | m_replyAutoApplied = applyReply(false); |
| 98 |
2/3✓ Branch 92 → 93 taken 1 time.
✗ Branch 92 → 94 not taken.
✓ Branch 93 → 94 taken 1 time.
|
1 | if (m_replyAutoApplied) rebuildCard(); |
| 99 | 4 | } | |
| 100 |