Sending HTML emails (or text emails) in BizTalk without embedded images has been done
to death – all you need is the RawString
message type.
But how do you use the SMTP adapter to send emails with embedded images?
I didn’t want to cheat and write a helper class.
I first looked at using the MIME/SMIME encoder pipeline component.
By default the MIME encoder uses a mime Content-Type of multipart/mixed.
So if you have a multipart message with an HTML body part and a single image as a
second part, this is what the MIME encoder generates:
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary=”–=_NextPart_001_000D_01C79667.1A2EEFB0″
Return-Path: donotreply@acme.com
This is a multi-part message in MIME format.
—-=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: text/html; charset=UTF-8;
charset=”utf-8″
Content-Transfer-Encoding: quoted-printable
Content-Location: http://localhost/
<html></html>
—-=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: image/gif
Content-Transfer-Encoding: base64
Content-Location: http://localhost/images/logo.gif
Content-ID: <http://localhost/images/logo.gif>
However, multipart/mixed is not how you embed content – you need to use multipart/related,
to indicate that all the parts in the message are related.
(Note: in order to view the output of the SMTP adapter I used the IIS SMTP
service, and configured the SMTP adapter to use an SMTP server of localhost,
and a To Address which matched my local domain. This will generate EML files
in InetpubmailrootDrop which you can open using Outlook Express (or notepad)).
There’s a little known MIME context property you can set called IsMultipartRelated.
If you use this, and have all your images as parts to the message, with your HTML
part as the body part, then the SMTP adapter will send a message which can be viewed
in a few email clients e.g. Outlook.
If you use this property, this is what the MIME encoder will output:
MIME-Version: 1.0
Content-Type: multipart/related;
type=”text/html”;
boundary=”–=_NextPart_001_000D_01C79667.1A2EEFB0″
Return-Path: donotreply@acme.com
This is a multi-part message in MIME format.
—-=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: text/html; charset=UTF-8;
charset=”utf-8″
Content-Transfer-Encoding: quoted-printable
Content-Location: http://localhost/
<html></html>
—-=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: image/gif
Content-Transfer-Encoding: base64
Content-Location: http://localhost/images/logo.gif
Content-ID: <http://localhost/images/logo.gif>
However this doesn’t work for any web based browsers e.g. Hotmail, Yahoo, SquirrelMail,
or LotusNotes.
Reading the RFC for Multipart
HTML messages showed me what I was missing: The Content-Type for the complete
message needs to be multipart/related, but the first (body) part needs to consist
of two parts, and have a Content-Type of multipart/alternative. – that is,
the first part is actually another multi-part message!
The reason for this is the body part contains a Text part, and an HTML part – so that
browsers which don’t understand HTML can still display the message.
It appears that a lot of email browsers won’t display messages unless they are in
this format and, in fact, this is what the RFC recommends.
Problem is, the MIME encoder does not support this (as far as I can make out – there’s
a promising property called PartContentTypeSecondaryHeader but I couldn’t get
it to work for me, plus IBaseMessagePart doesn’t act as a container for other
parts, so it’s unclear how you’d represent it in a message anyway).
So I wrote a pipeline component which takes an HTML stream as input, parses it and
downloads all the resources, and then does the correct MIME encoding.
This is the output from my component:
MIME-Version: 1.0
Content-Type: multipart/related;
type=”multipart/alternative”;
boundary=”–=_NextPart_001_000D_01C79667.1A2EEFB0″
Return-Path: donotreply@acme.com
This is a multi-part message in MIME format.
—-=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: multipart/alternative;
boundary=”–=_NextPart_001_000E_01C79667.1A2EEFB0″
—-=_NextPart_001_000E_01C79667.1A2EEFB0
Content-Type: text/plain;
charset=”iso-8859-1″
Content-Transfer-Encoding: 8bit
Plain text version of the email
—-=_NextPart_001_000E_01C79667.1A2EEFB0
Content-Type: text/html;
charset=”utf-8″
Content-Transfer-Encoding: quoted-printable
Content-Location: http://localhost/
<html></html>
—-=_NextPart_001_000E_01C79667.1A2EEFB0–
—-=_NextPart_001_000D_01C79667.1A2EEFB0
Content-Type: image/gif
Content-Transfer-Encoding: base64
Content-Location: http://localhost/images/logo.gif
Content-ID: <http://localhost/images/logo.gif>
The next trick was trying to get the SMTP adapter to accept the output from my component:
In BizTalk, there are two ways of MIME encoding content: simple encoding is performed
by the SMTP adapter (e.g. if you don’t use the MIME encoder); more advanced encoding
is performed by the MIME encoder.
So obviously the MIME encoder sets a property which indicates that the message is
already MIME encoded so the SMTP adapter doesn’t need to bother.
If you look at the list of context properties which the MIME encoder supports, you see
there’s one called IsMIMEEncoded.
Seems pretty obvious.
Except that the property doesn’t work.
I had to add a Debug pipeline component which would dump all the context properties
attached to a message after the MIME encoder had finished with it to find the answer.
Turns out there’s another context property called MimeEncoded (note the lack
of capitalisation), which is in the System namespace (not the MIME namespace) and
can only be set from code.
And this is the one that the SMTP adapter looks for. You can set it like this:
inmsg.Context.Write(“MimeEncoded”,
“http://schemas.microsoft.com/BizTalk/2003/system-properties”, true);
And now it works: I get email with embedded resources which can viewed correctly in
all mail browsers (except Lotus Notes, which occasionally doesn’t show images – but
that’s Notes for you!)
As an aside, whenever I write a pipeline component, I always try and have it work
in a streaming fashion (check out Christof
Claessens’ great article about this).
However when I reflected over the source of the MIME encoder, I noticed that it doesn’t
chain the streams – during the IComponent.Execute()call,
the encoder reads the streams of all parts and returns a new message with a single
part. So it’s not implemented in a streaming fashion (i.e. you can’t chain together
all the streams from all components).
What I haven’t covered in this post is how to use Content-ID and the cid: prefix
(in your html) to link the embedded resources to the parts in your mime message –
you can see examples of this in the RFC.