HTML Messages

A question that is asked frequently is:

How do you send HTML messages with Indy?

How you set up the TIdMessage component depends on what exactly you want the message to actually contain:

1) if you don’t want to add any attachments, and you don’t want to include a plain-text alternative for readers who don’t support HTML, then simply place the HTML code into the TIdMessage.Body property and set the TIdMessage.ContentType property to text/html.

2) if you do want to add attachments, or to include a plain-text alternative, then you need to ignore the TIdMessage.Body property altogether and use the TIdMessage.MessageParts collection instead. How you do that depends on which version of Indy you are using.

Indy 9

You instantiate two TIdText instances – the first containing the plain-text content and its ContentType property set to text/plain, and the second containing the HTML code and its ContentType property set to text/html.

The TIdMessage.ContentType property should be set to multipart/alternative, unless attachments are added, in which case the ContentType property should be either multipart/mixed or multipart/related, depending on whether the attachments are used with the HTML or not. To add attachments, instantiate TIdAttachment instead of TIdText. If the attachments are images or sounds that are being used by the HTML, then you also set the TIdAttachment.ContentID property to unique values that the HTML can reference via the “cid” protocol. For example, if the ContentID property of an image attachment is “image1” then the HTML needs to use “cid:image1” as the “src” attribute for any <img> element that displays that image attachment.

Unfortunately, Indy 9 does not handle the plaintext+html+images scenerio as well as it should. Indy 10 handles it better…

Indy 10

You do the same things as above, except that you use TIdAttachmentFile or TIdAttachmentMemory instead of TIdAttachment, and there are extra steps involved to set up the TIdText and TIdAttachmentFile/Memory instances properly.

Every message part has a ParentPart property that is set to -1 by default. This is an index to the part that is the PARENT of the current part. This is for nesting parts underneath each other, where -1 means that a part is not nested another another part but is instead located at the top-level of the message. Because of this nesting support, you end up with a variety of different setups depending on what exactly you want to send.

To make this task easier, I have created a set of helper classes to handle the following ugly details for you! All you have to do is set a few properties and the contents of a TIdMessage will be filled in appropriately. If you want to handle things manually, keep reading…

Plain-text and HTML and no attachments

Add two TIdText instances to the TIdMessage.MessageParts collection, as described above. Leave the ParentPart properties set to -1.

Set the TIdMessage.ContentType property to multipart/alternative.

You end up with the following TIdMessage layout:

TIdMessage (multipart/alternative)
{
  TIdText (text/plain), ParentPart -1
  TIdText (text/html), ParentPart -1
}

Here is a code snippet:

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'plain text goes here';
  ContentType := 'text/plain';
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'HTML goes here';
  ContentType := 'text/html';
end;

IdMessage1.ContentType := 'multipart/alternative';

HTML and non-related attachments and no plain-text

Non-related attachments are attachments that the user can click on and save out of the message. The message content does not refer to the attachments for any purpose.

Add a TIdText instance for the HTML content, and add TIdAttachmentFile/Memory instances for the attachments. Leave the ParentPart properties set to -1.

Set the TIdMessage.ContentType property to multipart/mixed.

You end up with the following TIdMessage layout:

TIdMessage (multipart/mixed)
{
  TIdText (text/html), ParentPart -1
  TIdAttachment (whatever), ParentPart -1
  TIdAttachment (whatever), ParentPart -1
  and so on...
}

Here is a code snippet:

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'HTML goes here';
  ContentType := 'text/html';
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image1.jpg') do begin
  ContentType := 'image/jpeg';
  FileName := 'image1.jpg';
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image2.jpg') do begin
  ContentType := 'image/jpeg';
  FileName := 'image2.jpg';
end;

IdMessage1.ContentType := 'multipart/mixed';

HTML and related attachments and no plain-text

Related attachments are grouped together with the HTML part so that the HTML content can reference them when needed. In order for HTML to reference any attachments, the HTML part and all of the related attachments need to be placed into a multipart/related section together so that they can find each other. The ContentType of the section must always have a type attribute that is set to the ContentType of the first nested child part. Each attachment also needs to have its ContentID property assigned to a unique value within the group. The HTML code can then use the “cid” protocol when referencing an attachment in the “src” attribute of any <img>, <sound>, and other such elements. For example, if an image has a ContentID of “image1” then the HTML code can refer to it as <img src=”cid:image1″>. The particular ContentID values are arbitrary and what they are does not matter just as long as they are each unique.

Add a TIdText instance for the HTML content. Leave the ParentPart properties set to -1.

Add TIdAttachmentFile/Memory instances for the attachments. Leave the ParentPart properties set to -1, and set the ContentID properties as desired.

Set the TIdMessage.ContentType property to multipart/related; type=”text/html”.

You end up with the following TIdMessage layout:

TIdMessage (multipart/related; type="text/html")
{
TIdText (text/html), ParentPart -1
TIdAttachment (image/*) ParentPart -1, ContentID 12345
TIdAttachment (image/*) ParentPart -1, ContentID 56789
and so on...
}

Here is a code snippet:

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'HTML goes here';
  ContentType := 'text/html';
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image1.jpg') do begin
  ContentID := '12345';
  ContentType := 'image/jpeg';
  FileName := 'image1.jpg';
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image2.jpg') do begin
  ContentID := '56789';
  ContentType := 'image/jpeg';
  FileName := 'image2.jpg';
end;

IdMessage1.ContentType := 'multipart/related; type="text/html"';

Plain-text and HTML and attachments

The layout depends on whether you are including HTML-related attachments or not, as follows:

HTML-related attachments only

Add a blank TIdText with its ContentType property set to multipart/alternative. Leave the ParentPart property set to -1.

Add TIdText instances for the plain-text and HTML contents. Set the ParentPart properties to the index of the ‘multipart/alternative’ part.

Add TIdAttachmentFile/Memory instances for the attachments. Leave the ParentPart properties set to -1, and set the ContentID properties as desired.

Set the TIdMessage.ContentType to multipart/related; type=”multipart/alternative”.

You end up with the following TIdMessage layout:

TIdMessage (multipart/related; type="multipart/alternative")
{
  TIdText (multipart/alternative), ParentPart -1
  {
    TIdText (text/plain), ParentPart 0
    TIdText (text/html), ParentPart 0
  }
  TIdAttachment (image/*) ParentPart -1, ContentID 12345
  TIdAttachment (image/*) ParentPart -1, ContentID 56789
  and so on...
}

Here is a code snippet:

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  ContentType := 'multipart/alternative';
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'plain textgoes here';
  ContentType := 'text/plain';
  ParentPart := 0;
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'HTML goes here';
  ContentType := 'text/html';
  ParentPart := 0;
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image1.jpg') do begin
  ContentID := '12345';
  ContentType := 'image/jpeg';
  FileName := 'image1.jpg';
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image2.jpg') do begin
  ContentID := '56789';
  ContentType := 'image/jpeg';
  FileName := 'image2.jpg';
end;

IdMessage1.ContentType := 'multipart/related; type="multipart/alternative"';

Non-related attachments only

Add a blank TIdText with its ContentType property set to multipart/alternative. Leave the ParentPart property set to -1.

Add TIdText instances for the plain-text and HTML contents. Set the ParentPart properties to the index of the multipart/alternative part.

Add TIdAttachmentFile/Memory instances for the attachments. Leave the ParentPart properties set to -1, and leave the ContentID properties empty.

Set the TIdMessage.ContentType property to multipart/mixed.

You end up with the following TIdMessage layout:

TIdMessage (multipart/mixed)
{
  TIdText (multipart/alternative), ParentPart -1
  {
    TIdText (text/plain), ParentPart 0
    TIdText (text/html), ParentPart 0
  }
  TIdAttachment (whatever) ParentPart -1
  TIdAttachment (whatever) ParentPart -1
  and so on...
}

Here is a code snippet:

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  ContentType := 'multipart/alternative';
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'plain textgoes here';
  ContentType := 'text/plain';
  ParentPart := 0;
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'HTML goes here';
  ContentType := 'text/html';
  ParentPart := 0;
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\archive.zip') do begin
  ContentType := 'application/x-zip-compressed';
  FileName := 'archive.zip';
end;

IdMessage1.ContentType := 'multipart/mixed';

Related and non-related attachments

You need to combine the two layouts above. You end up with the following TIdMessage layout:

TIdMessage (multipart/mixed)
{
  TIdText (multipart/related; type="multipart/alternative"), ParentPart -1
  {
    TIdText (multipart/alternative), ParentPart 0
    {
      TIdText (text/plain), ParentPart 1
      TIdText (text/html), ParentPart 1
    }
    TIdAttachment (image/*) ParentPart 0, ContentID 12345
    TIdAttachment (image/*) ParentPart 0, ContentID 56789
    and so on...
  }
  TIdAttachment (whatever) ParentPart -1
  TIdAttachment (whatever) ParentPart -1
  and so on...
}

Here is a code snippet:

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  ContentType := 'multipart/related; type="multipart/alternative"';
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  ContentType := 'multipart/alternative';
  ParentPart := 0;
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'plain textgoes here';
  ContentType := 'text/plain';
  ParentPart := 1;
end;

with TIdText.Create(IdMessage1.MessageParts, nil) do begin
  Body.Text := 'HTML goes here';
  ContentType := 'text/html';
  ParentPart := 1;
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image1.jpg') do begin
  ContentID := '12345';
  ContentType := 'image/jpeg';
  FileName := 'image1.jpg';
  ParentPart := 0;
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\image2.jpg') do begin
  ContentID := '56789';
  ContentType := 'image/jpeg';
  FileName := 'image2.jpg';
  ParentPart := 0;
end;

with TIdAttachmentFile.Create(IdMessage1.MessageParts, 'c:\folder\archive.zip') do begin
  ContentType := 'application/x-zip-compressed';
  FileName := 'archive.zip';
end;

IdMessage1.ContentType := 'multipart/mixed';