# Frequently Asked Questions
## Question Index
### General
* [Are MimeKit and MailKit completely free? Can I use them in my proprietary product(s)?](#completely-free)
* [Why do I get `NotSupportedException: No data is available for encoding ######. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.`?](#register-provider)
* [Why do I get a `TypeLoadException` when I try to create a new MimeMessage?](#type-load-exception)
### Messages
* [How do I create a message with attachments?](#create-attachments)
* [How do I get the main body of a message?](#message-body)
* [How do I tell if a message has attachments?](#has-attachments)
* [Why doesn't the `MimeMessage` class implement `ISerializable` so that I can serialize a message to disk and read it back later?](#serialize-message)
* [How do I parse messages?](#load-messages)
* [How do I save messages?](#save-messages)
* [How do I save attachments?](#save-attachments)
* [How do I get the email addresses in the From, To, and Cc headers?](#address-headers)
* [Why do attachments with unicode or long filenames appear as "ATT0####.dat" in Outlook?](#untitled-attachments)
* [How do I decrypt PGP messages that are embedded in the main message text?](#decrypt-inline-pgp)
* [How do I reply to a message using MimeKit?](#reply-message)
* [How do I forward a message?](#forward-message)
* [Why does text show up garbled in my ASP.NET Core / .NET Core / .NET 5 app?](#garbled-text)
### Specialty
* [How would I parse multipart/form-data from an HTTP web request?](#parse-web-request-form-data)
## General
### Q: Are MimeKit and MailKit completely free? Can I use them in my proprietary product(s)?
Yes. MimeKit and MailKit are both completely free and open source. They are both covered under the
[MIT](https://opensource.org/licenses/MIT) license.
### Q: Why do I get `NotSupportedException: No data is available for encoding ######. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.`?
In .NET Core, Microsoft decided to split out the non-Unicode text encodings into a separate NuGet package called
[System.Text.Encoding.CodePages](https://www.nuget.org/packages/System.Text.Encoding.CodePages).
MimeKit already pulls in a reference to this NuGet package, so you shsouldn't need to add a reference to it in
your project. That said, you will still need to register the encoding provider. It is recommended that you add
the following line of code to your program initialization (e.g. the beginning of your program's Main() method):
```csharp
System.Text.Encoding.RegisterProvider (System.Text.CodePagesEncodingProvider.Instance);
```
### Q: Why do I get a `TypeLoadException` when I try to create a new MimeMessage?
This only seems to happen in cases where the application is built for .NET Framework (v4.x) and seems to be most
common for ASP.NET web applications that were built using Visual Studio 2019 (it is unclear whether this happens
with Visual Studio 2022 as well).
The issue is that some (older?) versions of MSBuild do not correctly generate `\*.dll.config`, `app.config`
and/or `web.config` files with proper assembly version binding redirects.
If this problem is happening to you, make sure to use MimeKit and MailKit >= v4.0 which include `MimeKit.dll.config`
and `MailKit.dll.config`.
The next step is to manually edit your application's `app.config` (or `web.config`) to add a binding redirect
for `System.Runtime.CompilerServices.Unsafe`:
```xml
urls that refer to images embedded within the message with
// "file://" urls that the browser control will actually be able to load.
void HtmlTagCallback (HtmlTagContext ctx, HtmlWriter htmlWriter)
{
if (ctx.TagId == HtmlTagId.Meta && !ctx.IsEndTag) {
bool isContentType = false;
ctx.WriteTag (htmlWriter, false);
// replace charsets with "utf-8" since our output will be in utf-8 (and not whatever the original charset was)
foreach (var attribute in ctx.Attributes) {
if (attribute.Id == HtmlAttributeId.Charset) {
htmlWriter.WriteAttributeName (attribute.Name);
htmlWriter.WriteAttributeValue ("utf-8");
} else if (isContentType && attribute.Id == HtmlAttributeId.Content) {
htmlWriter.WriteAttributeName (attribute.Name);
htmlWriter.WriteAttributeValue ("text/html; charset=utf-8");
} else {
if (attribute.Id == HtmlAttributeId.HttpEquiv && attribute.Value != null
&& attribute.Value.Equals ("Content-Type", StringComparison.OrdinalIgnoreCase))
isContentType = true;
htmlWriter.WriteAttribute (attribute);
}
}
} else if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0) {
ctx.WriteTag (htmlWriter, false);
// replace the src attribute with a file:// URL
foreach (var attribute in ctx.Attributes) {
if (attribute.Id == HtmlAttributeId.Src) {
if (!TryGetImage (attribute.Value, out var image)) {
htmlWriter.WriteAttribute (attribute);
continue;
}
// Note: you can either use a "file://" URI or you can use a
// "data:" URI, the choice is yours.
var uri = GetFileUri (image, attribute.Value);
//var uri = GetDataUri (image);
htmlWriter.WriteAttributeName (attribute.Name);
htmlWriter.WriteAttributeValue (uri);
} else {
htmlWriter.WriteAttribute (attribute);
}
}
} else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag) {
ctx.WriteTag (htmlWriter, false);
// add and/or replace oncontextmenu="return false;"
foreach (var attribute in ctx.Attributes) {
if (attribute.Name.Equals ("oncontextmenu", StringComparison.OrdinalIgnoreCase))
continue;
htmlWriter.WriteAttribute (attribute);
}
htmlWriter.WriteAttribute ("oncontextmenu", "return false;");
} else {
// pass the tag through to the output
ctx.WriteTag (htmlWriter, true);
}
}
protected override void VisitTextPart (TextPart entity)
{
TextConverter converter;
// treat text/calendar parts as attachments rather than message bodies
if (entity.ContentType.IsMimeType ("text", "calendar")) {
calendarAattachments.Add (entity);
return;
}
if (body != null) {
// since we've already found the body, treat this as an attachment
attachments.Add (entity);
return;
}
if (entity.IsHtml) {
converter = new HtmlToHtml {
HtmlTagCallback = HtmlTagCallback
};
} else if (entity.IsFlowed) {
var flowed = new FlowedToHtml ();
string delsp;
if (entity.ContentType.Parameters.TryGetValue ("delsp", out delsp))
flowed.DeleteSpace = delsp.Equals ("yes", StringComparison.OrdinalIgnoreCase);
converter = flowed;
} else {
converter = new TextToHtml ();
}
body = converter.Convert (entity.Text);
}
protected override void VisitTnefPart (TnefPart entity)
{
// extract any attachments in the MS-TNEF part
attachments.AddRange (entity.ExtractAttachments ());
}
protected override void VisitMessagePart (MessagePart entity)
{
// treat message/rfc822 parts as attachments
attachments.Add (entity);
}
protected override void VisitMimePart (MimePart entity)
{
// realistically, if we've gotten this far, then we can treat this as an attachment
// even if the IsAttachment property is false.
attachments.Add (entity);
}
}
```
And the way you'd use this visitor might look something like this:
```csharp
void Render (MimeMessage message)
{
var tmpDir = Path.Combine (Path.GetTempPath (), message.MessageId);
var visitor = new HtmlPreviewVisitor (tmpDir);
Directory.CreateDirectory (tmpDir);
message.Accept (visitor);
DisplayHtml (visitor.HtmlBody);
DisplayAttachments (visitor.Attachments);
}
```
Once you've rendered the message using the above technique, you'll have a list of attachments that
were not used, even if they did not match the simplistic criteria used by the `MimeMessage.Attachments`
property.
### Q: Why doesn't the `MimeMessage` class implement `ISerializable` so that I can serialize a message to disk and read it back later?
The MimeKit API was designed to use the existing MIME format for serialization. In light of this, the ability
to use the .NET serialization API and format did not make much sense to support.
You can easily serialize a [MimeMessage](https://www.mimekit.net/docs/html/T_MimeKit_MimeMessage.htm) to a stream using the
[WriteTo](https://www.mimekit.net/docs/html/Overload_MimeKit_MimeMessage_WriteTo.htm) methods.
For more information on this topic, see the following other two topics:
* [How do I parse messages?](#load-messages)
* [How do I save messages?](#save-messages)
### Q: How do I parse messages?
One of the more common operations that MimeKit is meant for is parsing email messages from arbitrary streams.
There are two ways of accomplishing this task.
The first way is to use one of the [Load](https://www.mimekit.net/docs/html/Overload_MimeKit_MimeMessage_Load.htm) methods
on `MimeMessage`:
```csharp
// Load a MimeMessage from a stream
var message = MimeMessage.Load (stream);
```
Or you can load a message from a file path:
```csharp
// Load a MimeMessage from a file path
var message = MimeMessage.Load ("message.eml");
```
The second way is to use the [MimeParser](https://www.mimekit.net/docs/html/T_MimeKit_MimeParser.htm) class. For the most
part, using the `MimeParser` directly is not necessary unless you wish to parse a Unix mbox file stream. However, this is
how you would do it:
```csharp
// Load a MimeMessage from a stream
var parser = new MimeParser (stream, MimeFormat.Entity);
var message = parser.ParseMessage ();
```
For Unix mbox file streams, you would use the parser like this:
```csharp
// Load every message from a Unix mbox
var parser = new MimeParser (stream, MimeFormat.Mbox);
while (!parser.IsEndOfStream) {
var message = parser.ParseMessage ();
// do something with the message
}
```
### Q: How do I save messages?
One you've got a [MimeMessage](https://www.mimekit.net/docs/html/T_MimeKit_MimeMessage.htm), you can save
it to a file using the [WriteTo](https://mimekit.net/docs/html/Overload_MimeKit_MimeMessage_WriteTo.htm) method:
```csharp
message.WriteTo ("message.eml");
```
The `WriteTo` method also has overloads that allow you to write the message to a `Stream` instead.
By default, the `WriteTo` method will save the message using DOS line-endings on Windows and Unix
line-endings on Unix-based systems such as macOS and Linux. You can override this behavior by
passing a [FormatOptions](https://mimekit.net/docs/html/T_MimeKit_FormatOptions.htm) argument to
the method:
```csharp
// clone the default formatting options
var format = FormatOptions.Default.Clone ();
// override the line-endings to be DOS no matter what platform we are on
format.NewLineFormat = NewLineFormat.Dos;
message.WriteTo (format, "message.eml");
```
Note: While it may seem like you can safely use the `ToString` method to serialize a message,
***DON'T DO IT!*** This is ***not*** safe! MIME messages cannot be accurately represented as
strings due to the fact that each MIME part of the message *may* be encoded in a different
character set, thus making it impossible to convert the message into a unicode string using a
single charset to do the conversion (which is *exactly* what `ToString` does).
### Q: How do I save attachments?
If you've already got a [MimePart](https://www.mimekit.net/docs/html/T_MimeKit_MimePart.htm) that represents
the attachment that you'd like to save, here's how you might save it:
```csharp
using (var stream = File.Create (fileName))
attachment.Content.DecodeTo (stream);
```
Pretty simple, right?
But what if your attachment is actually a [MessagePart](https://www.mimekit.net/docs/html/T_MimeKit_MessagePart.htm)?
To save the content of a `message/rfc822` part, you'd use the following code snippet:
```csharp
using (var stream = File.Create (fileName))
attachment.Message.WriteTo (stream);
```
If you are iterating over all of the attachments in a message, you might do something like this:
```csharp
foreach (var attachment in message.Attachments) {
var fileName = attachment.ContentDisposition?.FileName ?? attachment.ContentType.Name;
using (var stream = File.Create (fileName)) {
if (attachment is MessagePart) {
var rfc822 = (MessagePart) attachment;
rfc822.Message.WriteTo (stream);
} else {
var part = (MimePart) attachment;
part.Content.DecodeTo (stream);
}
}
}
```
### Q: How do I get the email addresses in the From, To, and Cc headers?
The [From](https://www.mimekit.net/docs/html/P_MimeKit_MimeMessage_From.htm),
[To](https://www.mimekit.net/docs/html/P_MimeKit_MimeMessage_To.htm), and
[Cc](https://www.mimekit.net/docs/html/P_MimeKit_MimeMessage_Cc.htm) properties of a
[MimeMessage](https://www.mimekit.net/docs/html/T_MimeKit_MimeMessage.htm) are all of type
[InternetAddressList](https://www.mimekit.net/docs/html/T_MimeKit_InternetAddressList.htm). An
`InternetAddressList` is a list of
[InternetAddress](https://www.mimekit.net/docs/html/T_MimeKit_InternetAddress.htm) items. This is
where most people start to get lost because an `InternetAddress` is an abstract class that only
really has a [Name](https://www.mimekit.net/docs/html/P_MimeKit_InternetAddress_Name.htm) property.
As you've probably already discovered, the `Name` property contains the name of the person
(if available), but what you want is his or her email address, not their name.
To get the email address, you'll need to figure out what subclass of address each `InternetAddress`
really is. There are 2 subclasses of `InternetAddress`:
[GroupAddress](https://www.mimekit.net/docs/html/T_MimeKit_GroupAddress.htm) and
[MailboxAddress](https://www.mimekit.net/docs/html/T_MimeKit_MailboxAddress.htm).
A `GroupAddress` is a named group of more `InternetAddress` items that are contained within the
[Members](https://www.mimekit.net/docs/html/P_MimeKit_GroupAddress_Members.htm) property. To get
an idea of what a group address represents, consider the following examples:
```
To: My Friends: Joey
htmlWriter.WriteEndTag (HtmlTagId.BlockQuote);
// pass the
htmlWriter.WriteStartTag (HtmlTagId.BlockQuote); htmlWriter.WriteAttribute (HtmlAttributeId.Style, "border-left: 1px #ccc solid; margin: 0 0 0 .8ex; padding-left: 1ex;"); ctx.InvokeCallbackForEndTag = true; } } else { // pass the tag through to the output ctx.WriteTag (htmlWriter, true); } } string QuoteText (string text) { using (var quoted = new StringWriter ()) { quoted.WriteLine (GetOnDateSenderWrote (original)); using (var reader = new StringReader (text)) { string line; while ((line = reader.ReadLine ()) != null) { quoted.Write ("> "); quoted.WriteLine (line); } } return quoted.ToString (); } } protected override void VisitTextPart (TextPart entity) { string text; if (entity.IsHtml) { var converter = new HtmlToHtml { HtmlTagCallback = HtmlTagCallback }; text = converter.Convert (entity.Text); } else if (entity.IsFlowed) { var converter = new FlowedToText (); text = converter.Convert (entity.Text); text = QuoteText (text); } else { // quote the original message text text = QuoteText (entity.Text); } var part = new TextPart (entity.ContentType.MediaSubtype.ToLowerInvariant ()) { Text = text }; Push (part); } protected override void VisitMessagePart (MessagePart entity) { // don't descend into message/rfc822 parts } } ``` ```csharp public static MimeMessage Reply (MimeMessage message, MailboxAddress from, bool replyToAll) { var visitor = new ReplyVisitor (from, replyToAll); visitor.Visit (message); return visitor.Reply; } ``` ### Q: How do I forward a message? There are 2 common ways of forwarding a message: attaching the original message as an attachment and inlining the message body much like replying typically does. Which method you choose is up to you. To forward a message by attaching it as an attachment, you would do do something like this: ```csharp public static MimeMessage Forward (MimeMessage original, MailboxAddress from, IEnumerableto) { var message = new MimeMessage (); message.From.Add (from); message.To.AddRange (to); // set the forwarded subject if (!original.Subject?.StartsWith ("FW:", StringComparison.OrdinalIgnoreCase)) message.Subject = "FW: " + (original.Subject ?? string.Empty); else message.Subject = original.Subject; // create the main textual body of the message var text = new TextPart ("plain") { Text = "Here's the forwarded message:" }; // create the message/rfc822 attachment for the original message var rfc822 = new MessagePart { Message = original }; // create a multipart/mixed container for the text body and the forwarded message var multipart = new Multipart ("mixed"); multipart.Add (text); multipart.Add (rfc822); // set the multipart as the body of the message message.Body = multipart; return message; } ``` To forward a message by inlining the original message's text content, you can do something like this: ```csharp public static MimeMessage Forward (MimeMessage original, MailboxAddress from, IEnumerable to) { var message = new MimeMessage (); message.From.Add (from); message.To.AddRange (to); // set the forwarded subject if (!original.Subject?.StartsWith ("FW:", StringComparison.OrdinalIgnoreCase)) message.Subject = "FW: " + (original.Subject ?? string.Empty); else message.Subject = original.Subject; // quote the original message text using (var text = new StringWriter ()) { text.WriteLine (); text.WriteLine ("-------- Original Message --------"); text.WriteLine ("Subject: {0}", original.Subject ?? string.Empty); text.WriteLine ("Date: {0}", DateUtils.FormatDate (original.Date)); text.WriteLine ("From: {0}", original.From); text.WriteLine ("To: {0}", original.To); text.WriteLine (); text.Write (original.TextBody); message.Body = new TextPart ("plain") { Text = text.ToString () }; } return message; } ``` Keep in mind that not all messages will have a `TextBody` available, so you'll have to find a way to handle those cases. ### Q: Why does text show up garbled in my ASP.NET Core / .NET Core / .NET 5 app? .NET Core (and ASP.NET Core by extension) and .NET >= 5 only provide the Unicode encodings, ASCII and ISO-8859-1 by default. Other text encodings are not available to your application unless your application [registers](https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.registerprovider?view=net-5.0) the encoding provider that provides all of the additional encodings. First, add a package reference for the [System.Text.Encoding.CodePages](https://www.nuget.org/packages/System.Text.Encoding.CodePages) nuget package to your project and then register the additional text encodings using the following code snippet: ```csharp System.Text.Encoding.RegisterProvider (System.Text.CodePagesEncodingProvider.Instance); ``` Note: The above code snippet should be safe to call in .NET Framework versions >= 4.6 as well. ## Specialty ### Q: How would I parse multipart/form-data from an HTTP web request? Since classes like `HttpWebResponse` take care of parsing the HTTP headers (which includes the `Content-Type` header) and only offer a content stream to consume, MimeKit provides a way to deal with this using the following two static methods on `MimeEntity`: ```csharp public static MimeEntity Load (ParserOptions options, ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken)); public static MimeEntity Load (ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken)); ``` Here's how you might use these methods: ```csharp MimeEntity ParseMultipartFormData (HttpWebResponse response) { var contentType = ContentType.Parse (response.ContentType); return MimeEntity.Load (contentType, response.GetResponseStream ()); } ```