buildMessage static method
Builds the RFC 5322 message, ending with the . terminator.
Any body line starting with . is dot-stuffed, otherwise a line of .
inside the body would end the message early.
Implementation
static String buildMessage({
required String fromHeader,
required List<String> toHeaders,
required String subject,
required String text,
String? html,
Map<String, String> headers = const <String, String>{},
required DateTime date,
}) {
final buffer = StringBuffer();
void line(String value) => buffer.write('$value\r\n');
line('From: ${encodeAddressHeader(fromHeader)}');
line('To: ${toHeaders.map(encodeAddressHeader).join(', ')}');
line('Subject: ${encodeHeaderValue(subject)}');
line('Date: ${formatDate(date)}');
line('MIME-Version: 1.0');
for (final entry in headers.entries) {
line('${entry.key}: ${encodeHeaderValue(entry.value)}');
}
// Each part says how it is encoded. Without the header a body defaults
// to 7bit, which raw UTF-8 is not: a server without 8BITMIME may refuse
// it or strip the high bit, and the message arrives readable-ish, which
// is the worst outcome to be handed.
void part(String content) {
final bool plain = bodyIsSevenBit(content);
line('Content-Transfer-Encoding: ${plain ? '7bit' : 'quoted-printable'}');
line('');
// Dot-stuffed after encoding, because it is the lines that actually go
// down the wire that a leading dot would truncate.
buffer.write(_dotStuff(plain ? content : quotedPrintable(content)));
}
if (html == null) {
line('Content-Type: text/plain; charset=utf-8');
part(text);
} else {
const boundary = '----=_dartvel_alternative';
line('Content-Type: multipart/alternative; boundary="$boundary"');
line('');
line('--$boundary');
line('Content-Type: text/plain; charset=utf-8');
part(text);
line('');
line('--$boundary');
line('Content-Type: text/html; charset=utf-8');
part(html);
line('');
line('--$boundary--');
}
line('');
line('.');
return buffer.toString();
}