Posts Tagged ‘Attachments’

Processing applesingle and appledouble email attachments

Wednesday, October 1st, 2014

Mail.dll now supports applesingle and appledouble email attachments.

AppleSingle and AppleDouble are file formats developed to store Mac OS “dual-forked” files (data fork and resource fork)

AppleSingle combines both file forks and the related Finder meta-file information into a single file MIME entity, usually with application/applesingle content-type email header. Applesingle files are parsed by Mail.dll and data fork is extracted as a standard email attachment available through IMail.Attachments collection.

AppleDouble stores both forks as two separate files. Thus it uses multipart mime type – inside two MIME entities are stored: application/applefile, which contains resource fork only, and regular MIME attachment e.g. application/octet-stream content-type.

In both cases you simply use IMail.Attachments collection to access attachments.

byte[] eml = ...;

IMail mail = new MailBuilder().CreateFromEml(eml);
Assert.AreEqual(1, mail.Attachments.Count);
MimeData att = (MimeData) mail.Attachments[0];

byte[] data = att.Data; // data fork

You can control, if apple attachments should be processed using MailBuilder.ProcessAppleAutomatically property. When it is set to false applesingle files are not parsed and data fork is not extracted.

byte[] eml = ...;

MailBuilder builder = new MailBuilder();
builder.ProcessBinHexAutomatically = false;
IMail mail = builder.CreateFromEml(eml);

MimeAppleSingle appleSingle = (MimeAppleSingle) mail.Attachments[0];

Assert.AreEqual(ContentType.ApplicationAppleSingle, appleSingle.ContentType);

byte[] resource = appleSingle.AppleSingle.Resources;
string name = appleSingle.AppleSingle.RealName;

Process BinHex email attachments

Tuesday, September 9th, 2014

One of the recent Mail.dll email component updates brings the ability to process BinHex encoded email attachments.

BinHex is short for “binary-to-hexadecimal”. It is a binary to 7bit ASCII text encoding, that was used on the Mac OS for sending binary files through email. It is similar to Uuencode and Base64 encodings (both supported by Mail.dll), but it combines both “forks” (data and resources) of the Mac file.

As with all other data-to-ASCII encodings, BinHex files usually take more space than the original files, however as BinHex includes very simple run-length compression, encoded files may be smaller under some circumstances.

Below you can see such file inside a MIME entity:

Content-type: application/mac-binhex40; name="binhex.test.sit"
Content-disposition: attachment; filename="binhex.test.sit"

(This file must be converted with BinHex 4.0)

:$f*TEQKPH#jdCA0d,R0TG!"6594%8dP8)3#3"!&m!*!%EMa6593K!!%!!!&mFNa
[...]
Z"`#3!fKi!!!:

Mail.dll is going to extract data fork from such entity and put it in the IMail.Attachments collection, just like it was attached using standard MIME-way.

byte[] eml = ...;

IMail mail = new MailBuilder().CreateFromEml(eml);
Assert.AreEqual(1, mail.Attachments.Count);
MimeData att = (MimeData) mail.Attachments[0];

byte[] data = att.Data; // data fork

You can control, if BinHex attachments should be processed using MailBuilder.ProcessBinHexAutomatically property:

byte[] eml = ...;

MailBuilder builder = new MailBuilder();
builder.ProcessBinHexAutomatically = false;
IMail mail = builder.CreateFromEml(eml);
Assert.AreEqual(1, mail.Attachments.Count);
MimeBinHex binHex = (MimeBinHex) mail.Attachments[0];

byte[] data = binHex.BinHex.Data;
byte[] resource = binHex.BinHex.Resource;

string name = binHex.BinHex.Name;

As you can see, data fork is not extracted automatically and you can use MimeBinHex instance to access both data and resource forks and other information stored inside the BinHex encoded attachment.

Replace attachments in email message

Friday, September 27th, 2013

Attachments are not stored separately from message text and headers – they are embedded inside an email message. This, along with inefficient Base64 encoding is the most important reason of email messages being large in size. Mail.dll provides an easy way to replace attachments in existing messages:

// C#

IMail email = new MailBuilder().CreateFromEml(eml);
email.ReplaceAttachments();

' VB.NET

Dim email As IMail = New MailBuilder().CreateFromEml(eml)
email.ReplaceAttachments()

Each attachment will be replaced with the following text information: “This file (‘[FileName]’) containing [Size] bytes of data was removed.”. Thus making email much smaller in size.

ReplaceAttachmentsmethod has an overloaded version, that allows you to skip visual elements (content-disposition: inline) or/and alternative email representations. It also allows to specify text template and custom Tag, that can be used, for example, to create a custom url. This url can point to a place to which attachment was moved.

Within the template you can use [FileName], [Size] and [Tag] as template placeholders.

// C#

IMail email = Limilabs.Mail.Fluent.Mail
    .Text("body")
    .AddAttachment(new byte[] { 1, 2, 3 })
    .SetFileName("report.pdf")
    .Create();

AttachmentReplacerConfiguration configuration = new AttachmentReplacerConfiguration();
configuration.ReplaceVisuals = false;
configuration.Tag = 
    att => "http://example.com/" + email.MessageID + "/" + att.FileName;
configuration.Template = 
    "Attachment [FileName] removed. You can download it here: [Tag]";

email.ReplaceAttachments(configuration);
' VB.NET

Dim email As IMail = Limilabs.Mail.Fluent.Mail _
    .Text("body") _
    .AddAttachment(New Byte() {1, 2, 3}) _
    .SetFileName("report.pdf") _
    .Create()

Dim configuration As New AttachmentReplacerConfiguration()
configuration.ReplaceVisuals = False
configuration.Tag = Function(att)
    Return "http://example.com/" + email.MessageID + "/" + att.FileName
    End Function
configuration.Template = _
    "Attachment [FileName] removed. You can download it here: [Tag]"

email.ReplaceAttachments(configuration)

The following example illustrates the full process of downloading email from IMAP server,
creating new email, with the same information, but with all attachments replaced, uploading this message, and deleting original one:

// C#

using(Imap imap = new Imap())
{
    imap.ConnectSSL("imap.example.org");
    imap.UseBestLogin("user", "password");
    imap.SelectInbox();

    foreach (long uid in imap.GetAll())
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        if (email.Attachments.Count > 0)
        {
            email.ReplaceAttachments();

            imap.UploadMessage(email);

            imap.DeleteMessageByUID(uid);
        }
    }
    imap.Close();
}
' VB.NET

Using imap As New Imap()
   imap.ConnectSSL("imap.example.org")
   imap.UseBestLogin("user", "password")
   imap.SelectInbox()

   For Each uid As Long In imap.GetAll()
      Dim eml = imap.GetMessageByUID(uid)
      Dim email As IMail = New MailBuilder().CreateFromEml(eml)
      If email.Attachments.Count > 0 Then
         email.ReplaceAttachments()

         imap.UploadMessage(email)

         imap.DeleteMessageByUID(uid)
      End If
   Next
   imap.Close()
End Using

Remove attachments from email

Thursday, September 26th, 2013

First, there is one thing you must be aware of: neither POP3 nor IMAP protocol provide a way to remove attachments from existing emails. This is because email stored on the server is immutable. With IMAP protocol you can copy email message to different folder, you can apply some flags (\SEEN) to it, but you can’t change any part of the message.

The second important thing is, that attachments are not stored separately from the message text and headers – they are embedded inside the email.

Nevertheless Mail.dll provides an easy way to remove attachments from existing email message.

// C#

IMail email = new MailBuilder().CreateFromEml(eml);
email.RemoveAttachments();

' VB.NET

Dim email As IMail = New MailBuilder().CreateFromEml(eml)
email.RemoveAttachments()

RemoveAttachments method has an overloaded version, that allows you to skip visual elements (content-disposition: inline) or/and alternative email representations:

// C#

IMail email = new MailBuilder().CreateFromEml(eml);

AttachmentRemoverConfiguration configuration = new AttachmentRemoverConfiguration();
configuration.RemoveVisuals = false;

email.RemoveAttachments(configuration);

' VB.NET


Dim email As IMail = New MailBuilder().CreateFromEml(eml)

Dim configuration As New AttachmentRemoverConfiguration()
configuration.RemoveVisuals = False

email.RemoveAttachments(configuration)

The following example illustrates the full process of downloading email from IMAP server,
creating new email, with the same information, but without attachments, and finally uploading it, and removing the original message:

// C#

using(Imap imap = new Imap())
{
    imap.ConnectSSL("imap.example.org");
    imap.UseBestLogin("user", "password");
    imap.SelectInbox();

    foreach (long uid in imap.GetAll())
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        if (email.Attachments.Count > 0)
        {
            email.RemoveAttachments();

            imap.UploadMessage(email);

            imap.DeleteMessageByUID(uid);
        }
    }
    imap.Close();
}
' VB.NET

Using imap As New Imap()
   imap.ConnectSSL("imap.example.org")
   imap.UseBestLogin("user", "password")
   imap.SelectInbox()

   For Each uid As Long In imap.GetAll()
      Dim eml = imap.GetMessageByUID(uid)
      Dim email As IMail = New MailBuilder().CreateFromEml(eml)
      If email.Attachments.Count > 0 Then
         email.RemoveAttachments()

         imap.UploadMessage(email)

         imap.DeleteMessageByUID(uid)
      End If
   Next
   imap.Close()
End Using

Process emails embedded as attachments

Tuesday, January 11th, 2011

This article describes how to process emails embedded within emails as attachments. Mail.dll supports opening and extraction of embedded emails and attachments within.

Extracting all attachments

There is an easy way out – using ExtractAttachmentsFromInnerMessages method. It extracts all attachments from the email and from all attached messages. It returns easy to use collection of MimeData objects that represent each attachment.

// C#

using (Imap client = new Imap())
{
    client.ConnectSSL("imap.example.com");
    client.UseBestLogin("user", "password");
    client.SelectInbox();
    foreach (long uid in client.GetAll())
    {
        IMail mail = new MailBuilder().CreateFromEml(
            client.GetMessageByUID(uid));

        ReadOnlyCollection<MimeData> attachments = 
            mail.ExtractAttachmentsFromInnerMessages();
      
        foreach (MimeData mime in attachments)
        {
            mime.Save(@"c:\" + mime.SafeFileName);
        }
    }
    client.Close();
}
' VB.NET version

Using client As New Imap()
    client.ConnectSSL("imap.example.com")
    client.UseBestLogin("user", "password")
    client.SelectInbox()
    For Each uid As Long In client.GetAll()
        Dim mail As IMail = New MailBuilder() _
            .CreateFromEml(client.GetMessageByUID(uid))

        Dim attachments As ReadOnlyCollection(Of MimeData) _
            = mail.ExtractAttachmentsFromInnerMessages()

        For Each mime As MimeData In attachments
            mime.Save("c:\" + mime.SafeFileName)
        Next
           
    Next
    client.Close()
End Using

Manual approach

This gives you more control of which attachments to process, and which for some reason you wisch to ignore. We’ll create a simple method that processes all attached emails and extracts all attachments to specified folder.

First we’ll use Mail.dll IMAP component to download emails:

// C#

using (Imap client = new Imap())
{
    client.ConnectSSL("imap.example.com");
    client.UseBestLogin("user", "password");
    client.SelectInbox();
    foreach (long uid in client.GetAll())
    {
        IMail mail = new MailBuilder().CreateFromEml(
            client.GetMessageByUID(uid));

        SaveAttachmentsTo(mail, @"c:\tmp");
    }
    client.Close();
}
' VB.NET version

Using client As New Imap()
    client.ConnectSSL("imap.example.com")
    client.UseBestLogin("user", "password")
    client.SelectInbox()
    For Each uid As Long In client.GetAll()
        Dim mail As IMail = New MailBuilder() _
            .CreateFromEml(client.GetMessageByUID(uid))

        SaveAttachmentsTo(mail, @"c:\tmp")
    Next
    client.Close()
End Using

The following method traverses through all attachments. If regular attachment is found it is saved to specified folder. If email message is attached it parses it and saves all its attachments using recursion:

// C#

private void SaveAttachmentsTo(IMail mail, string folder)
{
    foreach (MimeData attachment in mail.Attachments)
    {
        if (attachment is IMailContainer)
        {
            IMail attachedMessage = ((IMailContainer) attachment).Message;
            SaveAttachmentsTo(attachedMessage, folder);
        }
        else
        {
            attachment.Save(
                Path.Combine(folder, attachment.SafeFileName));
        }
    }
}
' VB.NET version

Private Sub SaveAttachmentsTo(mail As IMail, folder As String)
    For Each attachment As MimeData In mail.Attachments
        If TypeOf attachment Is IMailContainer Then
            Dim attachedMessage As IMail = DirectCast(attachment, IMailContainer).Message
    	    SaveAttachmentsTo(attachedMessage, folder)
        Else
            attachment.Save( _
                Path.Combine(folder, attachment.SafeFileName))
        End If
    Next
End Sub