Header Folding Fix: Prevents invalid folding after < in headers, resolving common SpamAssassin errors like "Leading whitespace after '<'" and "unbalanced angle brackets".
Installation
Add this dependency your pubspec.yaml file:
dependencies:enough_mail:^2.2.0
API Documentation
Check out the full API documentation at https://pub.dev/documentation/enough_mail/latest/
High Level API Usage
The high level API abstracts away from IMAP and POP3 details, reconnects automatically and allows to easily watch a mailbox for new messages.
A simple usage example for using the high level API:
import'dart:io';
import'package:enough_mail/enough_mail.dart';
String userName = 'user.name';
String password = 'password';
voidmain() async {
awaitmailExample();
}
/// Builds a simple example messageMimeMessagebuildMessage() {
final builder = MessageBuilder.prepareMultipartAlternativeMessage(
plainText: 'Hello world!',
htmlText: '<p>Hello world!</p>',
)
..from = [MailAddress('Personal Name', 'sender@domain.com')]
..to = [
MailAddress('Recipient Personal Name', 'recipient@domain.com'),
MailAddress('Other Recipient', 'other@domain.com')
];
return builder.buildMimeMessage();
}
/// Builds an example message with attachmentFuture<MimeMessage> buildMessageWithAttachment() async {
final builder = MessageBuilder()
..from = [MailAddress('Personal Name', 'sender@domain.com')]
..to = [
MailAddress('Recipient Personal Name', 'recipient@domain.com'),
MailAddress('Other Recipient', 'other@domain.com')
]
..addMultipartAlternative(
plainText: 'Hello world!',
htmlText: '<p>Hello world!</p>',
);
final file = File.fromUri(Uri.parse('file://./document.pdf'));
await builder.addFile(file, MediaSubtype.applicationPdf.mediaType);
return builder.buildMimeMessage();
}
/// High level mail API exampleFuture<void> mailExample() async {
final email = '$userName@$domain';
print('discovering settings for $email...');
final config = awaitDiscover.discover(email);
if (config == null) {
// note that you can also directly create an account when// you cannot auto-discover the settings:// Compare the [MailAccount.fromManualSettings]// and [MailAccount.fromManualSettingsWithAuth]// methods for details.print('Unable to auto-discover settings for $email');
return;
}
print('connecting to ${config.displayName}.');
final account =
MailAccount.fromDiscoveredSettings('my account', email, password, config);
final mailClient = MailClient(account, isLogEnabled: true);
try {
await mailClient.connect();
print('connected');
final mailboxes =
await mailClient.listMailboxesAsTree(createIntermediate: false);
print(mailboxes);
await mailClient.selectInbox();
final messages = await mailClient.fetchMessages(count: 20);
messages.forEach(printMessage);
mailClient.eventBus.on<MailLoadEvent>().listen((event) {
print('New message at ${DateTime.now()}:');
printMessage(event.message);
});
await mailClient.startPolling();
// generate and send email:final mimeMessage = buildMessage();
await mailClient.sendMessage(mimeMessage);
} onMailExceptioncatch (e) {
print('High level API failed with $e');
}
}
Low Level Usage
A simple usage example for using the low level API:
import'dart:io';
import'package:enough_mail/enough_mail.dart';
String userName = 'user.name';
String password = 'password';
String imapServerHost = 'imap.domain.com';
int imapServerPort = 993;
bool isImapServerSecure = true;
String popServerHost = 'pop.domain.com';
int popServerPort = 995;
bool isPopServerSecure = true;
String smtpServerHost = 'smtp.domain.com';
int smtpServerPort = 465;
bool isSmtpServerSecure = true;
voidmain() async {
awaitdiscoverExample();
awaitimapExample();
awaitsmtpExample();
awaitpopExample();
exit(0);
}
Future<void> discoverExample() async {
var email = 'someone@enough.de';
var config = awaitDiscover.discover(email, isLogEnabled: false);
if (config == null) {
print('Unable to discover settings for $email');
} else {
print('Settings for $email:');
for (var provider in config.emailProviders) {
print('provider: ${provider.displayName}');
print('provider-domains: ${provider.domains}');
print('documentation-url: ${provider.documentationUrl}');
print('Incoming:');
print(provider.preferredIncomingServer);
print('Outgoing:');
print(provider.preferredOutgoingServer);
}
}
}
/// Low level IMAP API usage exampleFuture<void> imapExample() async {
final client = ImapClient(isLogEnabled: false);
try {
await client.connectToServer(imapServerHost, imapServerPort,
isSecure: isImapServerSecure);
await client.login(userName, password);
final mailboxes = await client.listMailboxes();
print('mailboxes: $mailboxes');
await client.selectInbox();
// fetch 10 most recent messages:final fetchResult = await client.fetchRecentMessages(
messageCount: 10, criteria: 'BODY.PEEK[]');
for (final message in fetchResult.messages) {
printMessage(message);
}
await client.logout();
} onImapExceptioncatch (e) {
print('IMAP failed with $e');
}
}
/// Low level SMTP API exampleFuture<void> smtpExample() async {
final client = SmtpClient('enough.de', isLogEnabled: true);
try {
await client.connectToServer(smtpServerHost, smtpServerPort,
isSecure: isSmtpServerSecure);
await client.ehlo();
if (client.serverInfo.supportsAuth(AuthMechanism.plain)) {
await client.authenticate('user.name', 'password', AuthMechanism.plain);
} elseif (client.serverInfo.supportsAuth(AuthMechanism.login)) {
await client.authenticate('user.name', 'password', AuthMechanism.login);
} else {
return;
}
final builder = MessageBuilder.prepareMultipartAlternativeMessage(
plainText: 'hello world.',
htmlText: '<p>hello <b>world</b></p>',
)
..from = [MailAddress('My name', 'sender@domain.com')]
..to = [MailAddress('Your name', 'recipient@domain.com')]
..subject = 'My first message';
final mimeMessage = builder.buildMimeMessage();
final sendResponse = await client.sendMessage(mimeMessage);
print('message sent: ${sendResponse.isOkStatus}');
} onSmtpExceptioncatch (e) {
print('SMTP failed with $e');
}
}
/// Low level POP3 API exampleFuture<void> popExample() async {
final client = PopClient(isLogEnabled: false);
try {
await client.connectToServer(popServerHost, popServerPort,
isSecure: isPopServerSecure);
await client.login(userName, password);
// alternative login:// await client.loginWithApop(userName, password); // optional different login mechanismfinal status = await client.status();
print(
'status: messages count=${status.numberOfMessages}, messages size=${status.totalSizeInBytes}');
final messageList = await client.list(status.numberOfMessages);
print(
'last message: id=${messageList?.first?.id} size=${messageList?.first?.sizeInBytes}');
var message = await client.retrieve(status.numberOfMessages);
printMessage(message);
message = await client.retrieve(status.numberOfMessages + 1);
print('trying to retrieve newer message succeeded');
await client.quit();
} onPopExceptioncatch (e) {
print('POP failed with $e');
}
}
voidprintMessage(MimeMessage message) {
print('from: ${message.from} with subject "${message.decodeSubject()}"');
if (!message.isTextPlainMessage()) {
print(' content-type: ${message.mediaType}');
} else {
final plainText = message.decodeTextPlainPart();
if (plainText != null) {
final lines = plainText.split('\r\n');
for (final line in lines) {
if (line.startsWith('>')) {
// break when quoted text startsbreak;
}
print(line);
}
}
}
}