email_indy.pdf

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 1

Upload: juan-carlos-magana-rodriguez

Post on 25-Oct-2015

43 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: email_indy.pdf

EMAIL MESSAGES FROM DELPHI. USING INDY TOSEND E-MAILS

1. Introduction

In this article we will discuss how to use INDY components to send e-mail messages frominside applications written in Delphi 7.Although the following example is based on Delphi 7, the same logic can be used from anyversions of Delphi. The following code was tested in Delphi 4, Delphi 7 and Indy 9. WhenIndy 10 is used please see enclosed samples for changes which has to be done toaccomplish 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 createe-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 someaddition 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 formsof 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 itdoes 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-textmessage 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 200416:31:50 -0800

1

Page 2: email_indy.pdf

02: Received: from ([000.00.00.000]) by smtp.mymail.com with MicrosoftSMTPSVC(5.0.2195.6713); Mon, 22 Mar 2004 16:36:25 -080003: From: [email protected]: Subject: TEST05: To: [email protected]: Date: Tue, 23 Mar 2004 13:52:41 -080007: X-Priority: 308: X-Library: Indy 9.00.1009: Return-Path: [email protected]: Message-ID: <[email protected]>11: X-OriginalArrivalTime: 23 Mar 2004 00:36:25.0250 (UTC)FILETIME=[DF543020:01C4106E]12:13: Hi, this is a test message14: .

Note #1: Code above is formatted to include line number, you will not see it in actuale-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 bedivided with a blank line (see bellow). The '.' in a last line is mandatory. It is a messageterminator, denoting the end of an email.

Note #3: When this sample is decoded by Indy, the message will be decoded and stored inthe TIdMessage.Body. Also TIdMessage.Encoding is set to meUU. This situation is changedas 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 likethis:

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 200416:31:50 -080002: Received: from ([000.00.00.000]) by smtp.mymail.com with MicrosoftSMTPSVC(5.0.2195.6713); Mon, 22 Mar 2004 16:36:25 -080003: From: [email protected]: Subject: TEST05: To: [email protected]: Date: Mon, 22 Mar 2004 16:30:06 -080007: X-Priority: 308: X-Library: Indy 9.00.1009: Return-Path: [email protected]: Message-ID: <[email protected]>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.014: Content-Transfer-Encoding: 7bit15:

2

Page 3: email_indy.pdf

16: This is a multi-part message in MIME format17:18: --=_NextPart_2rfkindysadvnqw3nerasdf19: Content-Type: text/plain20: Content-Transfer-Encoding: 7bit21:22: some text23:24: --=_NextPart_2rfkindysadvnqw3nerasdf25: Content-Type: image/jpg;26: name="htmlbodyIMG0000.JPG"27: Content-Transfer-Encoding: base6428: 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 aresignificant differences:Although the headers are the same, in a MIME-encoded emails the additional informationabout 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-mailclient 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-Mailclient 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 byMIME encoder and will be not used anywhere in visible text. Still you can specify it as anindication 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 isavailable starting INDY 10: there is a ConvertPreamble property which allows to turn adefault 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 usingINDY.

3

Page 4: email_indy.pdf

Please follow a next list to see how to create e-mails with different formats and abilities.

• 3.1 Simple E-Mail• 3.2 Composite E-Mail• 3.3 HTML Formatted E-Mail• 3.4 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 textmessage.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 welllMessage := TIdMessage.Create(Self);lMessage.From.Address := '[email protected]';lMessage.Subject := 'My test email';lMessage.Recipients.Add.Address := '[email protected]';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 filelMessage.SaveToFile('c:\1.txt', False); You will get a result similar to the one presented inNon-MIME encoded e-mails section.

Note. Code above does not include SMTP logic. Please refer to a sample project for thecomplete implementation.

3.2 Composite E-Mail

If you want to send a simple e-mail with attachment (see a code bellow) the result will bethe same. Even if you do not create TIdText MessagePart INDY will still convert yourlMessage.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 welllMessage := TIdMessage.Create(Self);lMessage.From.Address := '[email protected]';lMessage.Subject := 'My test email';lMessage.Recipients.Add.Address := '[email protected]';lMessage.Body.Text := 'some text';TIdAttachment.Create(lMessage.MessageParts, 'htmlbodyIMG0000.JPG');

4

Page 5: email_indy.pdf

// ... A code to send a messageend;In the case when you have attachments, Indy generates the MIME-encoded email, so themessage 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 welllMessage := TIdMessage.Create(Self);lMessage.From.Address := '[email protected]';lMessage.Subject := 'My test email';lMessage.Recipients.Add.Address := '[email protected]';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 hasMIME 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 secondsituation when message is streamed or saved into a file and a TIdText part created issuppressed. This was corrected in the next releases.Note #3: If your message is not a simple message with an attachment, please refer to theComplex 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 youre-mails. The easiest way of doing so is to use HTML format to manage styles. However, notall e-mail clients support this feature. "Customized" e-mails can be created by using amixed ('multipart/alternative') e-mail format. It is similar to the one from the prior chapterbut two instances of TIdText part will be used here: one for plain text (which will be usedfor 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 welllMessage := TIdMessage.Create(Self);lMessage.From.Address := '[email protected]';lMessage.Subject := 'My test email';lMessage.Recipients.Add.Address := '[email protected]';lMessage.Body.Clear;lTextPart := TIdText.Create(lMessage.MessageParts);lTextPart.Body.Text := 'This is a plain text message';lTextPart.ContentType := 'text/plain';

5

Page 6: email_indy.pdf

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 anyaddition objects into e-mail (for ex. pictures) please read HTML Formatted E-Mail withobjects.Note #2: Even if the plain-text part is not required, it is highly recommended to include itas a first part. This will allow clients that does not support multipart/alternative emails todisplay the plain-text version of the e-mail, so user can get some information about ane-mail.Note #3: You can try to use different ContentTypes (such as "text/rtf"), but it is notcommon 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. Bydefault such link will be blocked by an e-mail client and user will need specialoperation to download them.

• or as an embedded object - actual picture file will be passed as an attachment Fore-mail client correctly interpret such attachment 'multipart/related' e-mail formatshould be used. Also it is important that an e-mail should include a required partsand reference them via Content-ID header tag.

varlMessage: TIdMessage;lTextPart: TIdText;lImagePart: TIdAttachment;

begin// ... some code here to initialize your SMTP server.// It could be done somewhere as welllMessage := TIdMessage.Create(Self);lMessage.From.Address := '[email protected]';lMessage.Subject := 'My test email';lMessage.Recipients.Add.Address := '[email protected]';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="htmlbodyIMG0000.JPG" ></body></html>';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-mailcontent.

6

Page 7: email_indy.pdf

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 asregular attachments and then used by e-mail formatter. But if attachments are lost, notincluded or blocked on a way in, then text will have just empty placeholders for missingpictures.

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 Delphi7 it will set into 'multipart/mixed' instead of 'multipart/relative'. You will need latest Indy9.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 isespecially important when streaming is used.For ContentType it is highly recommended to set it manually or at least make sure it is setcorrectly.In latest versions next logic is default when Indy set ContentType based on message stateand parts (list represent a priority logic from highest to lowest - highest in a list has ahighest 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';

elseTIdMessage.ContentType := ''; // not used in e-mail

end;

It is recommended to verify a logic each time new e-mail type is created and follow nextrules 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 programmerstill need to pay attention and experiment with parts and their sequence to make it workcorrectly. In version 10 please refer to a ParentPart property which also allow to handlesome 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 notrecommended 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.

7

Page 8: email_indy.pdf

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

5.2 Changing the message-level encoding

It is possible to specify global message encoding using TIdMessage.Encoding property. Thissetting will affect default e-mail encoding interpretation and usually handled automaticallybased 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 asplain text between lines "------- Start of text attachment -------" and "------- End oftext 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 youre-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 ofTIdText 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 meXXencoded emails.ContentTransfer property accept next values '7bit', 'base64' or 'quoted-printable'. There isno 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 onyour 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 tofind how.

8

Page 9: email_indy.pdf

6. Comments

6.1 Message streaming

Working with messages from Delphi sometimes it is useful be able to save a content ofe-mail into a file or stream.It is possible to do with INDY but there is some problems in current version of a componentimplementation available in Delphi 7.

1. In original INDY for Delphi 7, streaming mechanism is broken in some situation. Seea "Standard streaming" code from a sample project - streamed composite e-mailwill be broken. A solution is implemented in "Fixed Streaming" action or use newerversion of INDY from your application.

2. In last stable release of INDY 9 (available from web-site) a stream passed intoTIdMessage.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 canpass a TFileStream or TMemoryStream, but do not expect to use it after method isinvoked. If you want fix this do it yourself, copy a code of a method in yourapplication and change it by adding next line after LIOHS is created:LIOHS.FreeStreams := False;. You can also use latest dev release which has thisissue 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'vesaved - there might be an extra part for default message body. Then it should beremoved before e-mail could be sent.

4. Standard version of INDY included in Delphi 7 does not support headers inattachments being streamed. Update to a latest version might help in this situation.

5. Similar problem exists for addition information stored in TIdPart.Headers whenrestored 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 checkNEVRONA/INDY web-site for more information.Be aware what latest version 10 has some number of introduced issues. Check a bug listbefore you will decide to update.

6.2 Trailing dot (the message terminator)

Being RFC complaint INDY routines depend on the correct RFC formatted message structureand "." (dot) is important part of it. Each message should be denoted by a '.' on a line byitself 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, youmay need to manually edit the stream or programmatically add it to the file before you readit.

7. Links

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

9

Page 10: email_indy.pdf

About the Author

Serge Dosyukov is a founder of Dragon Software, a consulting company which provides aconsulting and training services for Delphi, MSSQL Server, Wise Installation System andWise for Windows Installer since 1996.For information about on-site training and consulting you can contact author via email Sergeor visit his Web site at www.dragonsoft.us.

Copyright © 2008 Serge DosyukovALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORMWITHOUT THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR

10