10

I'm trying to access to an IMAP account using MailKit (created by jstedfast)

I manage to download the message (as a MimeMessage), and at some point I need to "forward" it to another account.

How would be the best way to do it, in order to preserve all the information of the original email (adresses, headers, body content, etc).

Thanks!

Community
  • 1
  • 1
Xavi Ivars
  • 634
  • 8
  • 22

1 Answers1

26

Different people mean different things when they say "forward", so I guess I'll provide answers to the different meanings that I can think of.

1. Forward (Resend) the message without any changes.

By "no changes", I literally mean no changes at all to the raw message data. The way to do this is to do:

var message = FetchMessageFromImapServer ();

using (var client = new SmtpClient ()) {
    client.Connect ("smtp.example.com", 465, true);
    client.Authenticate ("username", "password");

    var sender = new MailboxAddress ("My Name", "username@example.com");
    var recipients = new [] { new MailboxAddress ("John Smith", "john@smith.com") };

    // This version of the Send() method uses the supplied sender and
    // recipients rather than getting them from the message's headers.
    client.Send (message, sender, recipients);

    client.Disconnect (true);
}

Note: Some webmail providers such as GMail, Hotmail/Office365, etc. might not allow you to use this approach as they might consider it to be email spoofing. These mail hosts might replace the From header with the name/email address of your account, so be aware of this potential issue.

Usually people don't mean this type of "forwarding", though. If they want to resend, usually they'll use the next method of resending.

2. Forward (Resend) the message the way that an automated filter might send it.

var message = FetchMessageFromImapServer ();

// clear the Resent-* headers in case this message has already been Resent...
message.ResentSender = null;
message.ResentFrom.Clear ();
message.ResentReplyTo.Clear ();
message.ResentTo.Clear ();
message.ResentCc.Clear ();
message.ResentBcc.Clear ();

// now add our own Resent-* headers...
message.ResentFrom.Add (new MailboxAddress ("MyName", "username@example.com"));
message.ResentReplyTo.Add (new MailboxAddress ("MyName", "username@example.com"));
message.ResentTo.Add (new MailboxAddress ("John Smith", "john@smith.com"));
message.ResentMessageId = MimeUtils.GenerateMessageId ();
message.ResentDate = DateTimeOffset.Now;

using (var client = new SmtpClient ()) {
    client.Connect ("smtp.example.com", 465, true);
    client.Authenticate ("username", "password");

    // The Send() method will use the Resent-From/To/Cc/Bcc headers if
    // they are present.
    client.Send (message);

    client.Disconnect (true);
}

3. Forward the message by attaching it (in whole) to a new message, the way some email clients might do it.

var messageToForward = FetchMessageFromImapServer ();

// construct a new message
var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("MyName", "username@example.com"));
message.ReplyTo.Add (new MailboxAddress ("MyName", "username@example.com"));
message.To.Add (new MailboxAddress ("John Smith", "john@smith.com"));
message.Subject = "FWD: " + messageToForward.Subject;

// now to create our body...
var builder = new BodyBuilder ();
builder.TextBody = "Hey John,\r\n\r\nHere's that message I was telling you about...\r\n";
builder.Attachments.Add (new MessagePart { Message = messageToForward });

message.Body = builder.ToMessageBody ();

using (var client = new SmtpClient ()) {
    client.Connect ("smtp.example.com", 465, true);
    client.Authenticate ("username", "password");

    client.Send (message);

    client.Disconnect (true);
}

4. Forward the message "inline" the way many other email clients do it.

var messageToForward = FetchMessageFromImapServer ();

// construct a new message
var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("MyName", "username@example.com"));
message.ReplyTo.Add (new MailboxAddress ("MyName", "username@example.com"));
message.To.Add (new MailboxAddress ("John Smith", "john@smith.com"));
message.Subject = "FWD: " + messageToForward.Subject;

// now to create our body...
var builder = new BodyBuilder ();

using (var writer = new StringWriter ()) {
    var sender = messageToForward.From.Mailboxes.FirstOrDefault () ?? messageToForward.Sender;
    var senderName = sender != null ? (!string.IsNullOrEmpty (sender.Name) ? sender.Name : sender.Address) : "someone";
    var text = messageToForward.TextBody ?? string.Empty;

    writer.WriteLine ("On {0}, {1} wrote:", messageToForward.Date, senderName);

    using (var reader = new StringReader (text)) {
        string line;

        while ((line = reader.ReadLine ()) != null) {
            writer.Write ("> ");
            writer.WriteLine (line);
        }
    }

    builder.TextBody = writer.ToString ();
}

message.Body = builder.ToMessageBody ();

using (var client = new SmtpClient ()) {
    client.Connect ("smtp.example.com", 465, true);
    client.Authenticate ("username", "password");

    client.Send (message);

    client.Disconnect (true);
}
jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • 2
    Amazin reply. Thanks a lot! – Xavi Ivars Apr 07 '15 at 09:05
  • @jstedfast Is method 3 how most clients, 'quote the original message' in a new Reply? This should be added to the fancy new [http://www.mimekit.net](http://www.mimekit.net) (Looks good! Yet strangely familiar) – Goldfish Sandwich Jun 04 '15 at 21:07
  • Yea, most clients probably use #3 - I'd probably use #3 unless I was writing a mail filter to forward messages to another account automatically, in which case I'd probably use #1 or #2 (probably #1). The mimekit.net website is basically just the bootstrap carousel template with a little inspiration from the Json.NET website :-) – jstedfast Jun 05 '15 at 10:48
  • 2
    There's a 4th way too - IMAP can write to folders, so if you have a message you've retrieved with MailKit, you can store it in another IMAP folder (even in a different account, on a different host), using ImapClient.Inbox.Append method. This avoids SMTP entirely and the message is left entirely intact (possibly with "invalid" headers), but can be useful in some workflows. So it's not technically a forward, but it might well be what some viewers of this thread actually are trying to achieve. – slippyr4 Oct 13 '15 at 19:11
  • 1
    Using #2 (Resend) - the message is also delivered to original recipient as duplicity. This can create infinite loop when processing mailbox. To avoid this clear To header (message.To.clear();). – petriq Jul 16 '18 at 14:54
  • Did you set ResentSender or ResentFrom headers? If so, then MailKit's SmtpClient will use the ResentTo, ResentCc and ResentBcc properties to figure out who to send the message to, but if you do not set either of those, then it will fall back to To/Cc/Bcc. I suspect that's what you did wrong (I just verified). – jstedfast Jul 16 '18 at 15:54
  • #2 does not work, at least for gmail / Google mail. No errors, looks like all succeed but gmail just silently ignores. – jjxtra Aug 08 '18 at 20:57
  • #4 is missing attachments, how to add them? – ppau2004 Mar 07 '21 at 00:57