presentation/QuickComposeForwardAttachments.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "QuickComposeForwardAttachments.h" | ||
| 2 | |||
| 3 | #include "data/AttachmentStore.h" | ||
| 4 | #include "data/MailCache.h" | ||
| 5 | #include "presentation/QuickComposeBackend.h" | ||
| 6 | |||
| 7 | #include <QFileInfo> | ||
| 8 | #include "service/Rfc2822Builder.h" | ||
| 9 | |||
| 10 | #include <QDir> | ||
| 11 | #include <QFile> | ||
| 12 | #include <QLoggingCategory> | ||
| 13 | #include <QTemporaryDir> | ||
| 14 | |||
| 15 | Q_DECLARE_LOGGING_CATEGORY(lcQuickBackend) | ||
| 16 | |||
| 17 | namespace QuickComposeForwardAttachments { | ||
| 18 | |||
| 19 | 6 | QStringList exportTo(const MailCache &reader, qint64 folderId, qint64 uid, | |
| 20 | const QString &scratchDir) { | ||
| 21 | 6 | QStringList exported; | |
| 22 |
1/1✓ Branch 3 → 4 taken 6 times.
|
6 | const auto parts = reader.attachments().attachments(folderId, uid); |
| 23 |
1/1✓ Branch 4 → 5 taken 6 times.
|
6 | const QDir dir(scratchDir); |
| 24 | 6 | qint64 total = 0; | |
| 25 | 6 | QStringList skipped; | |
| 26 |
2/2✓ Branch 105 → 7 taken 3 times.
✓ Branch 105 → 106 taken 6 times.
|
9 | for (const Attachment &part : parts) { |
| 27 |
3/6✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 3 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 16 taken 3 times.
|
6 | if (part.size > Rfc2822Builder::MaxAttachmentBytes - total || |
| 28 | 3 | exported.size() >= 16) { | |
| 29 | ✗ | skipped << part.filename; | |
| 30 | ✗ | continue; | |
| 31 | } | ||
| 32 |
1/1✓ Branch 17 → 18 taken 3 times.
|
3 | const QByteArray blob = reader.attachments().attachmentData(part.id); |
| 33 |
1/2✗ Branch 19 → 20 not taken.
✓ Branch 19 → 22 taken 3 times.
|
3 | if (blob.isEmpty()) { |
| 34 | ✗ | skipped << part.filename; | |
| 35 | ✗ | continue; | |
| 36 | } | ||
| 37 | QString safeName = | ||
| 38 |
2/2✓ Branch 25 → 26 taken 3 times.
✓ Branch 28 → 29 taken 3 times.
|
3 | QString(part.filename).remove(QLatin1Char('/')).remove(QLatin1Char('\\')); |
| 39 |
4/8✓ Branch 32 → 33 taken 3 times.
✗ Branch 32 → 39 not taken.
✓ Branch 35 → 36 taken 3 times.
✗ Branch 35 → 39 not taken.
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 3 times.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 52 taken 3 times.
|
3 | if (safeName.isEmpty() || safeName == QLatin1String(".") || safeName == QLatin1String("..")) |
| 40 | ✗ | safeName = QStringLiteral("attachment-%1.bin").arg(part.id); | |
| 41 | // S106-254: one subdirectory per attachment — two parts of one mail may | ||
| 42 | // share a file name ("a/b" and "ab" collide after the slash removal); | ||
| 43 | // the second used to overwrite the first, and the forward sent one | ||
| 44 | // file twice. The file keeps its name, which becomes the part's name. | ||
| 45 |
2/2✓ Branch 53 → 54 taken 3 times.
✓ Branch 54 → 55 taken 3 times.
|
3 | const QString slot = dir.filePath(QString::number(exported.size())); |
| 46 |
2/2✓ Branch 56 → 57 taken 3 times.
✓ Branch 57 → 58 taken 3 times.
|
3 | const QString file = QDir(slot).filePath(safeName); |
| 47 |
1/1✓ Branch 59 → 60 taken 3 times.
|
3 | QFile out(file); |
| 48 |
9/19✓ Branch 61 → 62 taken 3 times.
✓ Branch 62 → 63 taken 3 times.
✓ Branch 63 → 64 taken 3 times.
✗ Branch 63 → 70 not taken.
✓ Branch 65 → 66 taken 3 times.
✓ Branch 66 → 67 taken 3 times.
✗ Branch 66 → 70 not taken.
✗ Branch 69 → 70 not taken.
✓ Branch 69 → 71 taken 3 times.
✓ Branch 72 → 73 taken 3 times.
✗ Branch 72 → 74 not taken.
✓ Branch 74 → 75 taken 3 times.
✗ Branch 74 → 76 not taken.
✗ Branch 76 → 77 not taken.
✓ Branch 76 → 79 taken 3 times.
✗ Branch 149 → 150 not taken.
✗ Branch 149 → 151 not taken.
✗ Branch 153 → 154 not taken.
✗ Branch 153 → 155 not taken.
|
6 | if (!QDir().mkpath(slot) || !out.open(QIODevice::NewOnly | QIODevice::WriteOnly) || |
| 49 |
1/1✓ Branch 67 → 68 taken 3 times.
|
3 | out.write(blob) != blob.size()) { |
| 50 | ✗ | skipped << part.filename; | |
| 51 | ✗ | continue; | |
| 52 | } | ||
| 53 | 3 | total += blob.size(); | |
| 54 |
1/1✓ Branch 80 → 81 taken 3 times.
|
3 | exported << file; |
| 55 | 3 | } | |
| 56 |
1/2✗ Branch 107 → 108 not taken.
✓ Branch 107 → 123 taken 6 times.
|
6 | if (!skipped.isEmpty()) |
| 57 | ✗ | qCInfo(lcQuickBackend) << "Forward skipped attachments beyond the budget:" | |
| 58 | ✗ | << skipped.join(QLatin1String(", ")); | |
| 59 | 6 | return exported; | |
| 60 | 6 | } | |
| 61 | |||
| 62 | 5 | std::shared_ptr<QTemporaryDir> makeScratch(const QString &profileDirectory) { | |
| 63 | return std::make_shared<QTemporaryDir>( | ||
| 64 |
3/3✓ Branch 2 → 3 taken 5 times.
✓ Branch 6 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
|
10 | QDir(profileDirectory).filePath(QStringLiteral("forward-attachments/XXXXXX"))); |
| 65 | } | ||
| 66 | |||
| 67 | ✗ | void loadSendAttachments(Rfc2822Builder &builder, const QStringList &attachments, | |
| 68 | QString &failedAttachment, bool &attachmentBudgetExceeded) { | ||
| 69 | ✗ | qint64 attachmentTotal = 0; | |
| 70 | ✗ | for (const QString &path : attachments) { | |
| 71 | ✗ | const QFileInfo info(path); | |
| 72 | ✗ | const qint64 size = info.size(); | |
| 73 | ✗ | if (!info.isFile() || !info.isReadable() || size < 0) { | |
| 74 | ✗ | failedAttachment = path; | |
| 75 | ✗ | break; | |
| 76 | } | ||
| 77 | ✗ | if (size > QuickComposeBackend::MaxComposeAttachmentsBytes - attachmentTotal) { | |
| 78 | ✗ | attachmentBudgetExceeded = true; | |
| 79 | ✗ | break; | |
| 80 | } | ||
| 81 | ✗ | attachmentTotal += size; | |
| 82 | ✗ | } | |
| 83 | // The size pass rejects an oversized set before reading any file. The | ||
| 84 | // actual loaded-byte pass also enforces the cap if a file grows meanwhile. | ||
| 85 | ✗ | if (failedAttachment.isEmpty() && !attachmentBudgetExceeded) { | |
| 86 | ✗ | attachmentTotal = 0; | |
| 87 | ✗ | for (const QString &path : attachments) { | |
| 88 | ✗ | const qint64 remaining = QuickComposeBackend::MaxComposeAttachmentsBytes - attachmentTotal; | |
| 89 | ✗ | qint64 loadedBytes = 0; | |
| 90 | ✗ | if (!builder.addAttachmentFromFile( | |
| 91 | ✗ | path, qMin(Rfc2822Builder::MaxAttachmentBytes, remaining), | |
| 92 | &loadedBytes)) { | ||
| 93 | ✗ | if (QFileInfo(path).size() > remaining) | |
| 94 | ✗ | attachmentBudgetExceeded = true; | |
| 95 | else | ||
| 96 | ✗ | failedAttachment = path; | |
| 97 | ✗ | break; | |
| 98 | } | ||
| 99 | ✗ | attachmentTotal += loadedBytes; | |
| 100 | } | ||
| 101 | } | ||
| 102 | ✗ | } | |
| 103 | |||
| 104 | } // namespace QuickComposeForwardAttachments | ||
| 105 |