You are on page 1of 10

EMAIL MESSAGES FROM DELPHI.

USING INDY TO SEND E-MAILS


1. Introduction
In this article we will discuss how to use INDY components to send e-mail messages from inside applications written in Delphi 7. Although the following example is based on Delphi 7, the same logic can be used from any versions of Delphi. The following code was tested in Delphi 4, Delphi 7 and Indy 9. When Indy 10 is used please see enclosed samples for changes which has to be done to accomplish the same tasks.

1.1 Requirements
The code samples are provided for Delphi 7 (Indy 9) and Delphi 2006 (Indy 10). You can use Delphi 2006 sample if upgraded to Indy 10 inside Delphi 7.

2. E-Mail formats
If you are familiar with a structure of e-mail messages then you can jump to How to create e-mail part. Usually any e-mail has 3 major parts: Header - contains information about a subject of the e-mail, who and when sent it, what program sent an e-mail, who should receive it, priority of an e-mail and some addition tracking info. Divider - blank line. It separates header and an e-mail body. Message body - contains a text of a message (optional, could be empty) and/or attachment(s). Another thing should be taken in consideration is a format of an e-mail. There are two forms of e-mails: non-MIME encoded emails - generally used to send plain-text, simple emails MIME-encoded emails - more common for e-mails that have attachments and for e-mails with formatted text (HTML/RTF based)). If your e-mail is properly formatted it will be accepted by any modern e-mail client. If it does not follow a guidelines an e-mail client might reject it or show it incorrectly.

Non-MIME encoded emails


The following is an e-mail sample of non-mime encoded type that contains a plain-text message without attachments: 01: Received: from smtp.mymail.com ([000.00.00.00]) by exchange.mydomain with SMTP (Microsoft Exchange Internet Mail Service Version 5.5.2653.13); Mon, 22 Mar 2004 16:31:50 -0800

02: Received: from ([000.00.00.000]) by smtp.mymail.com with Microsoft SMTPSVC(5.0.2195.6713); Mon, 22 Mar 2004 16:36:25 -0800 03: From: serge@domain.com 04: Subject: TEST 05: To: serge@domain.com 06: Date: Tue, 23 Mar 2004 13:52:41 -0800 07: X-Priority: 3 08: X-Library: Indy 9.00.10 09: Return-Path: serge@domain.com 10: Message-ID: <2KRELAYJwnzsLqIfQdm00000032@smtp.mymail.com> 11: X-OriginalArrivalTime: 23 Mar 2004 00:36:25.0250 (UTC) FILETIME=[DF543020:01C4106E] 12: 13: Hi, this is a test message 14: . Note #1: Code above is formatted to include line number, you will not see it in actual e-mail text. Note #2: The blank line between the header and the body (message) is important. Also if an e-mail includes any attachments then those will follow an e-email body and will be divided with a blank line (see bellow). The '.' in a last line is mandatory. It is a message terminator, denoting the end of an email. Note #3: When this sample is decoded by Indy, the message will be decoded and stored in the TIdMessage.Body. Also TIdMessage.Encoding is set to meUU. This situation is changed as soon as you add an attachment.

MIME encoded emails


This type of e-mails is completely different from the previous type. A simple one looks like this: 01: Received: from smtp.mymail.com ([000.00.00.00]) by exchange.mydomain with SMTP (Microsoft Exchange Internet Mail Service Version 5.5.2653.13); Mon, 22 Mar 2004 16:31:50 -0800 02: Received: from ([000.00.00.000]) by smtp.mymail.com with Microsoft SMTPSVC(5.0.2195.6713); Mon, 22 Mar 2004 16:36:25 -0800 03: From: serge@domain.com 04: Subject: TEST 05: To: serge@domain.com 06: Date: Mon, 22 Mar 2004 16:30:06 -0800 07: X-Priority: 3 08: X-Library: Indy 9.00.10 09: Return-Path: serge@domain.com 10: Message-ID: <SOMEJwnzsLqIfQdm00000032@smtp.mymail.com> 11: X-OriginalArrivalTime: 23 Mar 2004 00:36:25.0250 (UTC) FILETIME=[DF543020:01C4106E] 12: Content-Type: multipart/mixed; boundary="=_NextPart_2rfkindysadvnqw3nerasdf" 13: MIME-Version: 1.0 14: Content-Transfer-Encoding: 7bit 15:

16: This is a multi-part message in MIME format 17: 18: --=_NextPart_2rfkindysadvnqw3nerasdf 19: Content-Type: text/plain 20: Content-Transfer-Encoding: 7bit 21: 22: some text 23: 24: --=_NextPart_2rfkindysadvnqw3nerasdf 25: Content-Type: image/jpg; 26: name="htmlbodyIMG0000.JPG" 27: Content-Transfer-Encoding: base64 28: Content-Disposition: attachment; 29: filename="htmlbodyIMG0000.JPG" 30: 31: /9j/4AAQSkZJRgABAQAAAQABAAD/ 2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsK <skipped> WW: AKKKKACiiigD/9k= XX: YY: --=_NextPart_2rfkindysadvnqw3nerasdf-ZZ: . When compare this e-mail sample with one for non-MIME encoded emails there are significant differences: Although the headers are the same, in a MIME-encoded emails the additional information about Content-Type and MIME-Version is included (lines 12 and 13). It is followed by "prefix" text (it is called Preamble) and it is used in case if your e-mail client does not recognize MIME blocks or MIME encoding is disabled (line 16). Next are two MIME encoded parts: first contains a text which will be visible in your E-Mail client as a message body; and second is a part with file which is attached to an e-mail. When INDY receives and processes such e-mails, the following parts can be accessed: "prefix" is put in the TIdMessage.Body "part I" and "part II" are stored in a TIdMessage.MessageParts collection: "part I" is presented by object of type TIdText, and "part II" as TIdAttachment. TIdMessage.Encoding is set to meMIME Note #1: In e-mail clients with MIME encoding enabled/available, the "part I" is ignored by MIME encoder and will be not used anywhere in visible text. Still you can specify it as an indication for an e-mail client that e-mail body is stored in MIME blocks. Note #2: Indy 9 does not have an ability to override a Preamble text. This feature is available starting INDY 10: there is a ConvertPreamble property which allows to turn a default text "This is a multi-part message in MIME format" off (ConvertPreamble := False) and allows to use a Message Body to specify a text used by preamble block.

3. How to create emails


Now you are familiar with an e-mail structure and we can try to create a first e-mail using INDY.

Please follow a next list to see how to create e-mails with different formats and abilities. 3.1 3.2 3.3 3.4 Simple E-Mail Composite E-Mail HTML Formatted E-Mail HTML Formatted E-Mail with objects

Note. In the sample codes below we are going to work with MIME-enabled e-mails.

3.1 Simple E-Mail


As it was mentioned before, a simple e-mail does not contain anything other then a text message. To create an e-mail using INDY next operation should be performed. var lMessage: TIdMessage; begin // ... some code here to initialize your SMTP server. // It could be done somewhere as well lMessage := TIdMessage.Create(Self); lMessage.From.Address := 'myemail@mydomain.com'; lMessage.Subject := 'My test email'; lMessage.Recipients.Add.Address := 'someoneemail@somedomain.com'; lMessage.Body.Text := 'My message text'; // ... A code to send a message end; And if you want to see how your message will looks like you can just save it into a file lMessage.SaveToFile('c:\1.txt', False); You will get a result similar to the one presented in Non-MIME encoded e-mails section. Note. Code above does not include SMTP logic. Please refer to a sample project for the complete implementation.

3.2 Composite E-Mail


If you want to send a simple e-mail with attachment (see a code bellow) the result will be the same. Even if you do not create TIdText MessagePart INDY will still convert your lMessage.Body.Text into it automatically. Please try next and see yourself: var lMessage: TIdMessage; lTextPart: TIdText; begin // ... some code here to initialize your SMTP server. // It could be done somewhere as well lMessage := TIdMessage.Create(Self); lMessage.From.Address := 'myemail@mydomain.com'; lMessage.Subject := 'My test email'; lMessage.Recipients.Add.Address := 'someoneemail@somedomain.com'; lMessage.Body.Text := 'some text'; TIdAttachment.Create(lMessage.MessageParts, 'htmlbodyIMG0000.JPG');

// ... A code to send a message end; In the case when you have attachments, Indy generates the MIME-encoded email, so the message text has to go in a "text" part rather than the Body. The equivalent code is: var lMessage: TIdMessage; lTextPart: TIdText; begin // ... some code here to initialize your SMTP server. // It could be done somewhere as well lMessage := TIdMessage.Create(Self); lMessage.From.Address := 'myemail@mydomain.com'; lMessage.Subject := 'My test email'; lMessage.Recipients.Add.Address := 'someoneemail@somedomain.com'; lMessage.Body.Clear; lTextPart := TIdText.Create(lMessage.MessageParts); lTextPart.Body.Text := 'some text'; lTextPart.ContentType := 'text/plain'; lTextPart.ContentTransfer := '7bit'; TIdAttachment.Create(lMessage.MessageParts, 'htmlbodyIMG0000.JPG'); // ... A code to send a message end; Note #1: In any attempts to clear lMessage.Body, INDY will automatically set a default "prefix" text to "This is a multi-part message in MIME format" to indicate that an e-mail has MIME parts. lMessage.Body.Text will be ignored and standard preamble text will be used. Note #2: Delphi 7 includes version of INDY which does not work correctly in second situation when message is streamed or saved into a file and a TIdText part created is suppressed. This was corrected in the next releases. Note #3: If your message is not a simple message with an attachment, please refer to the Complex messages section.

3.3 HTML Formatted E-Mail


It is always nice to be able to add colors, different font's styles and sizes into a text of your e-mails. The easiest way of doing so is to use HTML format to manage styles. However, not all e-mail clients support this feature. "Customized" e-mails can be created by using a mixed ('multipart/alternative') e-mail format. It is similar to the one from the prior chapter but two instances of TIdText part will be used here: one for plain text (which will be used for not-HTML-enabled readers) and one with HTML version of it. var lMessage: TIdMessage; lTextPart: TIdText; begin // ... some code here to initialize your SMTP server. // It could be done somewhere as well lMessage := TIdMessage.Create(Self); lMessage.From.Address := 'myemail@mydomain.com'; lMessage.Subject := 'My test email'; lMessage.Recipients.Add.Address := 'someoneemail@somedomain.com'; lMessage.Body.Clear; lTextPart := TIdText.Create(lMessage.MessageParts); lTextPart.Body.Text := 'This is a plain text message'; lTextPart.ContentType := 'text/plain';

lTextPart := TIdText.Create(lMessage.MessageParts); lTextPart.Body.Text := '<html><body><b>This is a HTML message</b></body></html>'; lTextPart.ContentType := 'text/html'; // ... A code to send a message end; Note #1: HTML Formatted E-mail allows to format a text. If you need to include any addition objects into e-mail (for ex. pictures) please read HTML Formatted E-Mail with objects. Note #2: Even if the plain-text part is not required, it is highly recommended to include it as a first part. This will allow clients that does not support multipart/alternative emails to display the plain-text version of the e-mail, so user can get some information about an e-mail. Note #3: You can try to use different ContentTypes (such as "text/rtf"), but it is not common and not many e-mail client might support it.

3.4 HTML Formatted E-Mail with objects


Sometimes it is nice to have pictures in e-mail. It could be included in two ways: as a reference - link to a location on Internet where it could be downloaded from. By default such link will be blocked by an e-mail client and user will need special operation to download them. or as an embedded object - actual picture file will be passed as an attachment For e-mail client correctly interpret such attachment 'multipart/related' e-mail format should be used. Also it is important that an e-mail should include a required parts and reference them via Content-ID header tag.

var lMessage: TIdMessage; lTextPart: TIdText; lImagePart: TIdAttachment; begin // ... some code here to initialize your SMTP server. // It could be done somewhere as well lMessage := TIdMessage.Create(Self); lMessage.From.Address := 'myemail@mydomain.com'; lMessage.Subject := 'My test email'; lMessage.Recipients.Add.Address := 'someoneemail@somedomain.com'; lMessage.Body.Clear; lTextPart := TIdText.Create(lMessage.MessageParts); lTextPart.Body.Text := 'This is a plain text message'; lTextPart.ContentType := 'text/plain'; lTextPart := TIdText.Create(lMessage.MessageParts); lTextPart.Body.Text := '<html><body><b>This is a HTML message with picture</b><img src=" lTextPart.ContentType := 'text/html'; lImagePart := TIdAttachment.Create(lMessage.MessageParts, 'htmlbodyIMG0000.JPG'); lImagePart.ContentType := 'image/jpg'; lImagePart.Headers.Add('Content-ID: <htmlbodyIMG0000.JPG>'); // ... A code to send a message end; When message is received, it will have all required information for you to see an e-mail content.

Note: E-mail with embedded objects will be presented by client in very specific manner: picture files will be still visible as an attachments to a document and can be treated as regular attachments and then used by e-mail formatter. But if attachments are lost, not included or blocked on a way in, then text will have just empty placeholders for missing pictures.

4. Message and ContentType


It is very important to have correct ContentType setup for an e-mail. If ContentType is set to incorrect value this might stop correct interpretation of the e-mail. Sample project demonstrates this problem with HTML formatted e-mail: by default in Delphi 7 it will set into 'multipart/mixed' instead of 'multipart/relative'. You will need latest Indy 9.x or 10.x build to make it work. In such situation you need to check your e-mail settings before send it. And this is especially important when streaming is used. For ContentType it is highly recommended to set it manually or at least make sure it is set correctly. In latest versions next logic is default when Indy set ContentType based on message state and parts (list represent a priority logic from highest to lowest - highest in a list has a highest priority and suppress next ("case" operation)): case "Message-structure" of [the message has parts with a Content-ID header settings]: TIdMessage.ContentType := 'multipart/relative'; [the message has a TIdAttachment part]: TIdMessage.ContentType := 'multipart/mixed'; [the message has more than one TIdText part]: TIdMessage.ContentType := 'multipart/alternative'; else TIdMessage.ContentType := ''; // not used in e-mail end; It is recommended to verify a logic each time new e-mail type is created and follow next rules to determine what ContentType should be used: Composite HTML and Text parts e-mails - ContentType set to 'multipart/alternative' Composite HTML with objects - ContentType set to 'multipart/relative' Composite with attachments - ContentType set to 'multipart/mixed' Once again, this situation is very unstable and requires unique tuning in every situation. Latest version of 9.x and new 10.x fixed and simplify some of these issues, but programmer still need to pay attention and experiment with parts and their sequence to make it work correctly. In version 10 please refer to a ParentPart property which also allow to handle some of the issues with complex e-mails.

5. Message tuning
5.1 Changing the body encoding
By default a body text (preamble text for MIME encoded messages) is not encoded. It is not recommended to encode Body text of MIME encoded messages (parts 3.2.2, 3.3 and 3.4) because it could cause incorrect representation of the message by e-mail client.

In a case of simple messages (part 3.1) and simple messages with attachments (part 3.2.1) this is acceptable and TIdMessage.ContentTransferEncoding property could be used to specify encoding.

5.2 Changing the message-level encoding


It is possible to specify global message encoding using TIdMessage.Encoding property. This setting will affect default e-mail encoding interpretation and usually handled automatically based on a structure of e-mail. By default TIdMessage.Encoding = meDefault. But as soon as changes to e-mail are made it will be changed by system in next way: TIdMessage.Encoding = meUU, if document has no attachment TIdMessage.Encoding = meMIME, if document has no attachment. This is default value and you should avoid changing it (except for simple with attachment). When it is acceptable, you can set TIdMessage.Encoding to: meUU: The message body follows the headers attachments (TIdAttachment parts) are UU-encoded and any text parts (TIdText parts) are outputted in the body as plain text between lines "------- Start of text attachment -------" and "------- End of text attachment -------". meXX: Same as meUU, except attachment parts are XX-encoded meMIME: All text and attachment parts are inserted in MIME format, in individual boundary-separated parts. Your message will not have a body part "per say", instead all information will be presented as a MessageParts. Note. Avoid changing default settings until it is really necessary. This might cause your e-mail to be not recognized by e-mail client programs, especially if it is simple message.

5.3 Changing the part-level encoding


If different encoding is required for each message part then ContentTransfer property of TIdText or TIdAttachment could be used to set a part-level encoding. It could be used only in MIME-encoded emails and it is ignored for meUU and meXX encoded emails. ContentTransfer property accept next values '7bit', 'base64' or 'quoted-printable'. There is no predefined constants for these values.

X. Send E-mail using Outlook


It is not always necessary to use "low level" code to send e-mail. If you have Outlook on your system there is always a possibility to use it instead. Zarko Gajic has small code snippet which allows you to do this. Please follow next link to find how.

6. Comments
6.1 Message streaming
Working with messages from Delphi sometimes it is useful be able to save a content of e-mail into a file or stream. It is possible to do with INDY but there is some problems in current version of a component implementation available in Delphi 7. 1. In original INDY for Delphi 7, streaming mechanism is broken in some situation. See a "Standard streaming" code from a sample project - streamed composite e-mail will be broken. A solution is implemented in "Fixed Streaming" action or use newer version of INDY from your application. 2. In last stable release of INDY 9 (available from web-site) a stream passed into TIdMessage.SaveToStream is destroyed from inside a method. What this means is you cannot use a stream storage, to have access to a message information. You can pass a TFileStream or TMemoryStream, but do not expect to use it after method is invoked. If you want fix this do it yourself, copy a code of a method in your application and change it by adding next line after LIOHS is created: LIOHS.FreeStreams := False;. You can also use latest dev release which has this issue fixed. 3. Next problem with streaming appears when you using e-mails with attachments. After email is restored from the stream make sure you've got same parts you've saved - there might be an extra part for default message body. Then it should be removed before e-mail could be sent. 4. Standard version of INDY included in Delphi 7 does not support headers in attachments being streamed. Update to a latest version might help in this situation. 5. Similar problem exists for addition information stored in TIdPart.Headers when restored from the stream. In sample project TIdPart.Headers is used to specify "Content-ID" value. Check "Fixed Streaming" portion from sample for details. Some of these issues are addressed in latest releases of INDY 9 and INDY 10. Please check NEVRONA/INDY web-site for more information. Be aware what latest version 10 has some number of introduced issues. Check a bug list before you will decide to update.

6.2 Trailing dot (the message terminator)


Being RFC complaint INDY routines depend on the correct RFC formatted message structure and "." (dot) is important part of it. Each message should be denoted by a '.' on a line by itself to indicate an end of a message text (strictly, the CR LF '.' CR LF sequence or #13#10 .#13#10). If you loading an e-mail with incorrect structure, "." might be missing. In these cases, you may need to manually edit the stream or programmatically add it to the file before you read it.

7. Links
This article on the web This article on the BDN Delphi 7/2006 sample project

About the Author


Serge Dosyukov is a founder of Dragon Software, a consulting company which provides a consulting and training services for Delphi, MSSQL Server, Wise Installation System and Wise for Windows Installer since 1996. For information about on-site training and consulting you can contact author via email Serge or visit his Web site at www.dragonsoft.us. Copyright 2008 Serge Dosyukov ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR

10

You might also like