Cause of the problem
The problem seems to be caused by a specific way of sending mail attachments from Hotmail.
A code snippet above retrieves the attachment name using the Rebex.Net.ImapMessageInfo
object. This object is created from an appropriate message-info structure provided by an IMAP server. And an IMAP server retrieves the information from attachment headers.
An attachment header of an e-mail sent from common mailserver looks like this:
Content-Type: text/plain; name="file1.txt"
Content-Description: file1.txt
Content-Disposition: attachment; filename="file1.txt"; size=6;
creation-date="Mon, 10 Oct 2011 12:10:39 GMT";
modification-date="Mon, 10 Oct 2011 12:10:42 GMT"
Content-Transfer-Encoding: base64
While a header from an e-mail sent from Hotmail is following:
Content-Type: text/plain
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="file1.txt"
It looks like Hotmail somehow forgets to set the "name" parameter of the Content-Type header and it doesn't even set Content-Description. Because IMAP servers return the Content-Type header and Content-Description headers (but not Content-Desposition header) in response to FETCH (BODY) / FETCH (BODYSTRUCTURE), a properly-behaved IMAP server won't be able to send the filename to the client.
In short, even though it looks hard-to-believe, Hotmail attachments are incompatible with IMAP protocols's message structure retrieval functionality .
Workaround A (slow)
A simple solution would be to load the whole message (using Imap.GetMessage
method) instead of working with the ImapMessageInfo
only.
ImapMessageCollection col =
imap.GetMessageList(ImapListFields.Envelope | ImapListFields.MessageStructure);
foreach (ImapMessageInfo item in col)
{
Console.WriteLine("{0}", item.Subject);
var msg = imap.GetMailMessage(item.SequenceNumber);
foreach (Attachment attachment in item.Attachments)
Console.WriteLine(" {0}", attachment.FileName);
}
This solution is very slow because the whole mail message (including a content of the attachments) has to be loaded to the IMAP client.
Workaround B (faster)
There is a way to create an IMAP query to download message header only instead of the full message. The Rebex.Mime.MimeMessage
class can then retrieve an attachment filename from the Content-Disposition
part of the message header.
ImapMessageCollection col =
imap.GetMessageList(ImapListFields.Envelope | ImapListFields.MessageStructure);
foreach (ImapMessageInfo item in col)
{
Console.WriteLine("{0}", item.Subject);
foreach (ImapMessagePart part in item.GetParts())
{
if (part.Kind == ImapMessagePartKind.Attachment)
{
string partId = part.Id;
partId = partId.Split('/')[0] + ".MIME";
byte[] headers = imap.GetMessagePart(item.UniqueId, partId);
using (MemoryStream ms = new MemoryStream(headers))
{
MimeMessage mime = new MimeMessage();
mime.Options |= MimeOptions.OnlyParseHeaders;
mime.Load(ms);
Console.WriteLine(" {0}", mime.Name);
}
}
}
}
Even this workaround is not so fast as the original message retrieval using the ImapMessagePart.FileName
property, because a particular attachment headers have to be loaded from IMAP server. But loading just the headers is much faster than loading the whole message and attachments.
Unfortunately, we cannot implement this workaround into the Rebex IMAP/SSL component, since we cannot recognize the Hotmail mails well. However, we're planning to improve the Imap.GetMessagePart
method in order it can download the message headers only.