diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-20 18:41:21 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-20 18:41:21 +0200 |
| commit | 14e74f8f92ce949230e3008de8fd24d0c5a18153 (patch) | |
| tree | 1c9af4b288533652a40c1abc42201f1aa0e24ba8 /src/messagebuilder.h | |
| parent | 90cf14b0b23218f0ca01a280998e855a00b47567 (diff) | |
| download | qtmaildir-14e74f8f92ce949230e3008de8fd24d0c5a18153.tar.gz qtmaildir-14e74f8f92ce949230e3008de8fd24d0c5a18153.zip | |
feat(compose): build outgoing messages with GMime, item 123
One built message serves three consumers: the autosaved draft, the bytes on
the send command's stdin, and the sent copy. A draft is therefore
byte-identical to what would be sent.
Three GMime defaults are wrong for this application and each is corrected
explicitly, because all three fail only on accented text and this user
writes Italian:
GMime encodes as iso-8859-1 unless told otherwise, so the subject carries an
explicit utf-8 argument. g_mime_text_part_set_text() encodes with whatever
charset is set when it is CALLED, so setting the charset afterwards produces
a part labelled utf-8 carrying latin-1 bytes; the content stream is built
directly instead. And neither Date nor Message-ID is generated unless asked
for, and a message without a Message-ID cannot be threaded by anything that
receives it.
Attachments are checked at build time rather than at attach time: a file can
vanish in between, and a message missing the thing it was written to carry
must never reach the send command.
An account with no address fails the build rather than producing a message
with an empty From. Config::account() returns a default-constructed Account
for an unknown key rather than failing, so without that guard a bad key
would produce silently malformed mail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015muoUo2GdxmBDSp5vjYcbE
Diffstat (limited to 'src/messagebuilder.h')
| -rw-r--r-- | src/messagebuilder.h | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/src/messagebuilder.h b/src/messagebuilder.h new file mode 100644 index 0000000..f9de277 --- /dev/null +++ b/src/messagebuilder.h @@ -0,0 +1,59 @@ +/* + * qtmaildir - a Qt6 mail client for notmuch-indexed Maildirs + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include <QByteArray> +#include <QString> + +#include "types.h" + +struct Account; + +/// Turns an OutgoingMessage into the RFC822 bytes that get sent. +/// +/// ONE built message serves three consumers: the autosaved draft, the bytes on +/// the send command's stdin, and the sent copy. A draft is therefore +/// byte-identical to what would be sent. +/// +/// GMime rather than assembling RFC822 by string. The alternative means +/// reimplementing RFC 2047 header encoding, quoted-printable for accented +/// bodies, boundary uniqueness and line-length limits. This user writes +/// Italian; a body containing an accented character is every message, and a +/// bug there produces mail that looks correct locally and arrives as mojibake. +namespace MessageBuilder { + +struct Result +{ + QByteArray bytes; ///< The complete message. Empty on failure. + QString error; ///< Empty on success. + QString messageId; ///< The generated Message-ID, for the caller's records. + + bool ok() const { return error.isEmpty(); } +}; + +/// Builds \p message as sent from \p account. +/// +/// Fails, rather than sending a partial message, when an attachment named in +/// the message no longer exists. That is checked HERE, at build time, rather +/// than when the file was attached: a file can vanish in between, and the +/// failure must stop the send rather than produce a message missing the thing +/// it was written to carry. +Result build(const OutgoingMessage &message, const Account &account); + +} // namespace MessageBuilder |
