1346 lines
48 KiB
Plaintext
1346 lines
48 KiB
Plaintext
|
{
|
||
|
$Project$
|
||
|
$Workfile$
|
||
|
$Revision$
|
||
|
$DateUTC$
|
||
|
$Id$
|
||
|
|
||
|
This file is part of the Indy (Internet Direct) project, and is offered
|
||
|
under the dual-licensing agreement described on the Indy website.
|
||
|
(http://www.indyproject.org/)
|
||
|
|
||
|
Copyright:
|
||
|
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
|
||
|
}
|
||
|
{
|
||
|
$Log$
|
||
|
}
|
||
|
{
|
||
|
Rev 1.53 29/12/2004 11:01:56 CCostelloe
|
||
|
IsMsgSinglePartMime now cleared in TIdMessage.Clear.
|
||
|
|
||
|
Rev 1.52 28/11/2004 20:06:28 CCostelloe
|
||
|
Enhancement to preserve case of MIME boundary
|
||
|
|
||
|
Rev 1.51 10/26/2004 10:25:44 PM JPMugaas
|
||
|
Updated refs.
|
||
|
|
||
|
Rev 1.50 2004.10.26 9:10:00 PM czhower
|
||
|
TIdStrings
|
||
|
|
||
|
Rev 1.49 24.08.2004 18:01:44 Andreas Hausladen
|
||
|
Added AttachmentBlocked property to TIdAttachmentFile.
|
||
|
|
||
|
Rev 1.48 6/29/04 12:29:04 PM RLebeau
|
||
|
Updated TIdMIMEBoundary.FindBoundary() to check the string length after
|
||
|
calling Sys.Trim() before referencing the string data
|
||
|
|
||
|
Rev 1.47 6/9/04 5:38:48 PM RLebeau
|
||
|
Updated ClearHeader() to clear the MsgId and UID properties.
|
||
|
|
||
|
Updated SetUseNowForDate() to support AValue being set to False
|
||
|
|
||
|
Rev 1.46 16/05/2004 18:54:42 CCostelloe
|
||
|
New TIdText/TIdAttachment processing
|
||
|
|
||
|
Rev 1.45 03/05/2004 20:43:08 CCostelloe
|
||
|
Fixed bug where QP or base64 encoded text part got header encoding
|
||
|
incorrectly outputted as 8bit.
|
||
|
|
||
|
Rev 1.44 4/25/04 1:29:34 PM RLebeau
|
||
|
Bug fix for SaveToStream
|
||
|
|
||
|
Rev 1.42 23/04/2004 20:42:18 CCostelloe
|
||
|
Bug fixes plus support for From containing multiple addresses
|
||
|
|
||
|
Rev 1.41 2004.04.18 1:39:20 PM czhower
|
||
|
Bug fix for .NET with attachments, and several other issues found along the
|
||
|
way.
|
||
|
|
||
|
Rev 1.40 2004.04.16 11:30:56 PM czhower
|
||
|
Size fix to IdBuffer, optimizations, and memory leaks
|
||
|
|
||
|
Rev 1.39 14/03/2004 17:47:54 CCostelloe
|
||
|
Bug fix: quoted-printable attachment encoding was changed to base64.
|
||
|
|
||
|
Rev 1.38 2004.02.03 5:44:00 PM czhower
|
||
|
Name changes
|
||
|
|
||
|
Rev 1.37 2004.02.03 2:12:14 PM czhower
|
||
|
$I path change
|
||
|
|
||
|
Rev 1.36 26/01/2004 01:51:14 CCostelloe
|
||
|
Changed implementation of supressing BCC List generation
|
||
|
|
||
|
Rev 1.35 25/01/2004 21:15:42 CCostelloe
|
||
|
Added SuppressBCCListInHeader property for use by TIdSMTP
|
||
|
|
||
|
Rev 1.34 1/21/2004 1:17:14 PM JPMugaas
|
||
|
InitComponent
|
||
|
|
||
|
Rev 1.33 1/19/04 11:36:02 AM RLebeau
|
||
|
Updated GenerateHeader() to remove support for the BBCList property
|
||
|
|
||
|
Rev 1.32 16/01/2004 17:30:18 CCostelloe
|
||
|
Added support for BinHex4.0 encoding
|
||
|
|
||
|
Rev 1.31 11/01/2004 19:53:20 CCostelloe
|
||
|
Revisions for TIdMessage SaveToFile & LoadFromFile for D7 & D8
|
||
|
|
||
|
Rev 1.29 08/01/2004 23:43:40 CCostelloe
|
||
|
LoadFromFile/SaveToFile now work in D7 again
|
||
|
|
||
|
Rev 1.28 1/7/04 11:07:16 PM RLebeau
|
||
|
Bug fix for various TIdMessage properties that were not previously using
|
||
|
setter methods correctly.
|
||
|
|
||
|
Rev 1.27 08/01/2004 00:30:26 CCostelloe
|
||
|
Start of reimplementing LoadFrom/SaveToFile
|
||
|
|
||
|
Rev 1.26 21/10/2003 23:04:32 CCostelloe
|
||
|
Bug fix: removed AttachmentEncoding := '' in SetEncoding.
|
||
|
|
||
|
Rev 1.25 21/10/2003 00:33:04 CCostelloe
|
||
|
meMIME changed to meDefault in TIdMessage.Create
|
||
|
|
||
|
Rev 1.24 10/17/2003 7:42:54 PM BGooijen
|
||
|
Changed default Encoding to MIME
|
||
|
|
||
|
Rev 1.23 10/17/2003 12:14:08 AM DSiders
|
||
|
Added localization comments.
|
||
|
|
||
|
Rev 1.22 2003.10.14 9:57:04 PM czhower
|
||
|
Compile todos
|
||
|
|
||
|
Rev 1.21 10/12/2003 1:55:46 PM BGooijen
|
||
|
Removed IdStrings from uses
|
||
|
|
||
|
Rev 1.20 2003.10.11 10:01:26 PM czhower
|
||
|
.inc path
|
||
|
|
||
|
Rev 1.19 10/10/2003 10:42:26 PM BGooijen
|
||
|
DotNet
|
||
|
|
||
|
Rev 1.18 9/10/2003 1:50:54 PM SGrobety
|
||
|
DotNet
|
||
|
|
||
|
Rev 1.17 10/8/2003 9:53:12 PM GGrieve
|
||
|
use IdCharsets
|
||
|
|
||
|
Rev 1.16 05/10/2003 16:38:50 CCostelloe
|
||
|
Restructured MIME boundary output
|
||
|
|
||
|
Rev 1.15 2003.10.02 9:27:50 PM czhower
|
||
|
DotNet Excludes
|
||
|
|
||
|
Rev 1.14 01/10/2003 17:58:52 HHariri
|
||
|
More fixes for Multipart Messages and also fixes for incorrect transfer
|
||
|
encoding settings
|
||
|
|
||
|
Rev 1.12 9/28/03 1:36:04 PM RLebeau
|
||
|
Updated GenerateHeader() to support the BBCList property
|
||
|
|
||
|
Rev 1.11 26/09/2003 00:29:34 CCostelloe
|
||
|
IdMessage.Encoding now set when email decoded; XXencoded emails now decoded;
|
||
|
logic added to GenerateHeader
|
||
|
|
||
|
Rev 1.10 04/09/2003 20:42:04 CCostelloe
|
||
|
GenerateHeader sets From's Name field to Address field if Name blank;
|
||
|
trailing spaces removed after boundary in FindBoundary; force generation of
|
||
|
InReplyTo header.
|
||
|
|
||
|
Rev 1.9 29/07/2003 01:14:30 CCostelloe
|
||
|
In-Reply-To fixed in GenerateHeader
|
||
|
|
||
|
Rev 1.8 11/07/2003 01:11:02 CCostelloe
|
||
|
GenerateHeader changed from function to procedure, results now put in
|
||
|
LastGeneratedHeaders. Better for user (can see headers sent) and code still
|
||
|
efficient.
|
||
|
|
||
|
Rev 1.7 10/07/2003 22:39:00 CCostelloe
|
||
|
Added LastGeneratedHeaders field and modified GenerateHeaders so that a copy
|
||
|
of the last set of headers generated for this message is maintained (see
|
||
|
comments starting "CC")
|
||
|
|
||
|
Rev 1.6 2003.06.23 9:46:54 AM czhower
|
||
|
Russian, Ukranian support for headers.
|
||
|
|
||
|
Rev 1.5 6/3/2003 10:46:54 PM JPMugaas
|
||
|
In-Reply-To header now supported.
|
||
|
|
||
|
Rev 1.4 1/27/2003 10:07:46 PM DSiders
|
||
|
Corrected error setting file stream permissions in LoadFromFile. Bug Report
|
||
|
649502.
|
||
|
|
||
|
Rev 1.3 27/1/2003 3:07:10 PM SGrobety
|
||
|
X-Priority header only added if priority <> mpNormal (because of spam filters)
|
||
|
|
||
|
Rev 1.2 09/12/2002 18:19:00 ANeillans Version: 1.2
|
||
|
Removed X-Library Line that was causing people problems with spam detection
|
||
|
software , etc.
|
||
|
|
||
|
Rev 1.1 12/5/2002 02:53:56 PM JPMugaas
|
||
|
Updated for new API definitions.
|
||
|
|
||
|
Rev 1.0 11/13/2002 07:56:52 AM JPMugaas
|
||
|
|
||
|
2004-05-04 Ciaran Costelloe
|
||
|
- Replaced meUU with mePlainText. This also meant that UUE/XXE encoding was pushed
|
||
|
down from the message-level to the MessagePart level, where it belongs.
|
||
|
|
||
|
2004-04-20 Ciaran Costelloe
|
||
|
- Added support for multiple From addresses (per RFC 2822, section 3.6.2) by
|
||
|
adding a FromList field. The previous From field now maps to FromList[0].
|
||
|
|
||
|
2003-10-04 Ciaran Costelloe (see comments starting CC4)
|
||
|
|
||
|
2003-09-20 Ciaran Costelloe (see comments starting CC2)
|
||
|
- Added meDefault, meXX to TIdMessageEncoding.
|
||
|
Code now sets TIdMessage.Encoding when it decodes an email.
|
||
|
Modified TIdMIMEBoundary to work as a straight stack, now Push/Pops ParentPart also.
|
||
|
Added meDefault, meXX to TIdMessageEncoding.
|
||
|
Moved logic from SendBody to GenerateHeader, added extra logic to avoid exceptions:
|
||
|
Change any encodings we dont know to base64
|
||
|
We dont support attachments in an encoded body, change it to a supported combination
|
||
|
Made changes to support ConvertPreamble and MIME message bodies with a
|
||
|
ContentTransferEncoding of base64, quoted-printable.
|
||
|
ProcessHeaders now decodes BCC list.
|
||
|
|
||
|
2003-09-02 Ciaran Costelloe
|
||
|
- Added fix to FindBoundary suggested by Juergen Haible to remove trailing space
|
||
|
after boundary added by some clients.
|
||
|
|
||
|
2003-07-10 Ciaran Costelloe
|
||
|
- Added LastGeneratedHeaders property, see comments starting CC. Changed
|
||
|
GenerateHeader from function to procedure, it now puts the generated headers
|
||
|
into LastGeneratedHeaders, which is where dependant units should take the
|
||
|
results from. This ensures that the headers that were generated are
|
||
|
recorded, which some users' programs may need.
|
||
|
|
||
|
2002-12-09 Andrew Neillans
|
||
|
- Removed X-Library line
|
||
|
|
||
|
2002-08-30 Andrew P.Rybin
|
||
|
- Now InitializeISO is IdMessage method
|
||
|
|
||
|
2001-12-27 Andrew P.Rybin
|
||
|
Custom InitializeISO, ExtractCharSet
|
||
|
|
||
|
2001-Oct-29 Don Siders
|
||
|
Added EIdMessageCannotLoad exception.
|
||
|
Added RSIdMessageCannotLoad constant.
|
||
|
Added TIdMessage.LoadFromStream.
|
||
|
Modified TIdMessage.LoadFromFile to call LoadFromStream.
|
||
|
Added TIdMessage.SaveToStream.
|
||
|
Modified TIdMessage.SaveToFile to call SaveToStream.
|
||
|
Modified TIdMessage.GenerateHeader to include headers received but not used in properties.
|
||
|
|
||
|
2001-Sep-14 Andrew Neillans
|
||
|
Added LoadFromFile Header only
|
||
|
|
||
|
2001-Sep-12 Johannes Berg
|
||
|
Fixed upper/Sys.LowerCase in uses clause for Kylix
|
||
|
|
||
|
2001-Aug-09 Allen O'Neill
|
||
|
Added line to check for valid charset value before adding second ';' after content-type boundry
|
||
|
|
||
|
2001-Aug-07 Allen O'Neill
|
||
|
Added SaveToFile & LoadFromFile ... Doychin fixed
|
||
|
|
||
|
2001-Jul-11 Hadi Hariri
|
||
|
Added Encoding for both MIME and UU.
|
||
|
|
||
|
2000-Jul-25 Hadi Hariri
|
||
|
- Added support for MBCS
|
||
|
|
||
|
2000-Jun-10 Pete Mee
|
||
|
- Fixed some minor but annoying bugs.
|
||
|
|
||
|
2000-May-06 Pete Mee
|
||
|
- Added coder support directly into TIdMessage.
|
||
|
}
|
||
|
|
||
|
unit IdMessage;
|
||
|
|
||
|
{
|
||
|
2001-Jul-11 Hadi Hariri
|
||
|
|
||
|
TODO: Make checks for encoding and content-type later on.
|
||
|
TODO: Add TIdHTML, TIdRelated
|
||
|
TODO: CountParts on the fly
|
||
|
TODO: Merge Encoding and AttachmentEncoding
|
||
|
TODO: Make encoding plugable
|
||
|
TODO: Clean up ISO header coding
|
||
|
}
|
||
|
|
||
|
{ TODO : Moved Decode/Encode out and will add later,. Maybe TIdMessageEncode, Decode?? }
|
||
|
|
||
|
{ TODO : Support any header in TMessagePart }
|
||
|
|
||
|
{ DESIGN NOTE: The TIdMessage has an fBody which should only ever be the
|
||
|
raw message. TIdMessage.fBody is only raw if TIdMessage.fIsEncoded = true
|
||
|
|
||
|
The component parts are thus possibly made up of the following
|
||
|
order of TMessagePart entries:
|
||
|
|
||
|
MP[0] : Possible prologue text (fBoundary is '')
|
||
|
|
||
|
MP[0 or 1 - depending on prologue existence] :
|
||
|
fBoundary = boundary parameter from Content-Type
|
||
|
|
||
|
MP[next...] : various parts with or without fBoundary = ''
|
||
|
|
||
|
MP[MP.Count - 1] : Possible epilogue text (fBoundary is '')
|
||
|
}
|
||
|
|
||
|
{ DESIGN NOTE: If TMessagePart.fIsEncoded = True, then TMessagePart.fBody
|
||
|
is the encoded raw message part. Otherwise, it is the (decoded) text.
|
||
|
}
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$I IdCompilerDefines.inc}
|
||
|
|
||
|
uses
|
||
|
Classes,
|
||
|
IdAttachment,
|
||
|
IdBaseComponent,
|
||
|
IdCoderHeader,
|
||
|
IdEMailAddress,
|
||
|
IdExceptionCore,
|
||
|
IdHeaderList,
|
||
|
IdMessageParts;
|
||
|
|
||
|
type
|
||
|
TIdMessagePriority = (mpHighest, mpHigh, mpNormal, mpLow, mpLowest);
|
||
|
|
||
|
const
|
||
|
ID_MSG_NODECODE = False;
|
||
|
ID_MSG_USESNOWFORDATE = True;
|
||
|
ID_MSG_PRIORITY = mpNormal;
|
||
|
|
||
|
type
|
||
|
TIdMIMEBoundary = class(TObject)
|
||
|
protected
|
||
|
FBoundaryList: TStrings;
|
||
|
{CC: Added ParentPart as a TStrings so I dont have to create a TIntegers}
|
||
|
FParentPartList: TStrings;
|
||
|
function GetBoundary: string;
|
||
|
function GetParentPart: integer;
|
||
|
public
|
||
|
constructor Create;
|
||
|
destructor Destroy; override;
|
||
|
procedure Push(ABoundary: string; AParentPart: integer);
|
||
|
procedure Pop;
|
||
|
procedure Clear;
|
||
|
function Count: integer;
|
||
|
|
||
|
property Boundary: string read GetBoundary;
|
||
|
property ParentPart: integer read GetParentPart;
|
||
|
end;
|
||
|
|
||
|
TIdMessageFlags =
|
||
|
( mfAnswered, //Message has been answered.
|
||
|
mfFlagged, //Message is "flagged" for urgent/special attention.
|
||
|
mfDeleted, //Message is "deleted" for removal by later EXPUNGE.
|
||
|
mfDraft, //Message has not completed composition (marked as a draft).
|
||
|
mfSeen, //Message has been read.
|
||
|
mfRecent ); //Message is "recently" arrived in this mailbox.
|
||
|
|
||
|
TIdMessageFlagsSet = set of TIdMessageFlags;
|
||
|
|
||
|
{WARNING: Replaced meUU with mePlainText in Indy 10 due to meUU being misleading.
|
||
|
This is the MESSAGE-LEVEL "encoding", really the Sys.Format or layout of the message.
|
||
|
When encoding, the user can let Indy decide on the encoding by leaving it at
|
||
|
meDefault, or he can pick meMIME or mePlainText }
|
||
|
|
||
|
//TIdMessageEncoding = (meDefault, meMIME, meUU, meXX);
|
||
|
TIdMessageEncoding = (meDefault, meMIME, mePlainText);
|
||
|
|
||
|
TIdInitializeIsoEvent = procedure (var VHeaderEncoding: Char;
|
||
|
var VCharSet: string) of object;
|
||
|
|
||
|
TIdMessage = class;
|
||
|
|
||
|
TIdCreateAttachmentEvent = procedure(const AMsg: TIdMessage;
|
||
|
const AHeaders: TStrings; var AAttachment: TIdAttachment) of object;
|
||
|
|
||
|
TIdMessage = class(TIdBaseComponent)
|
||
|
protected
|
||
|
FAttachmentTempDirectory: string;
|
||
|
FBccList: TIdEmailAddressList;
|
||
|
FBody: TStrings;
|
||
|
FCharSet: string;
|
||
|
FCcList: TIdEmailAddressList;
|
||
|
FContentType: string;
|
||
|
FContentTransferEncoding: string;
|
||
|
FContentDisposition: string;
|
||
|
FDate: TDateTime;
|
||
|
FIsEncoded : Boolean;
|
||
|
FExtraHeaders: TIdHeaderList;
|
||
|
FEncoding: TIdMessageEncoding;
|
||
|
FFlags: TIdMessageFlagsSet;
|
||
|
FFromList: TIdEmailAddressList;
|
||
|
FHeaders: TIdHeaderList;
|
||
|
FMessageParts: TIdMessageParts;
|
||
|
FMIMEBoundary: TIdMIMEBoundary;
|
||
|
FMsgId: string;
|
||
|
FNewsGroups: TStrings;
|
||
|
FNoEncode: Boolean;
|
||
|
FNoDecode: Boolean;
|
||
|
FOnInitializeISO: TIdInitializeISOEvent;
|
||
|
FOrganization: string;
|
||
|
FPriority: TIdMessagePriority;
|
||
|
FSubject: string;
|
||
|
FReceiptRecipient: TIdEmailAddressItem;
|
||
|
FRecipients: TIdEmailAddressList;
|
||
|
FReferences: string;
|
||
|
FInReplyTo : String;
|
||
|
FReplyTo: TIdEmailAddressList;
|
||
|
FSender: TIdEMailAddressItem;
|
||
|
FUID: String;
|
||
|
FXProgram: string;
|
||
|
FOnCreateAttachment: TIdCreateAttachmentEvent;
|
||
|
FLastGeneratedHeaders: TIdHeaderList;
|
||
|
FConvertPreamble: Boolean;
|
||
|
FSavingToFile: Boolean;
|
||
|
FIsMsgSinglePartMime: Boolean;
|
||
|
FExceptionOnBlockedAttachments: Boolean; // used in TIdAttachmentFile
|
||
|
//
|
||
|
procedure DoInitializeISO(var VHeaderEncoding: Char; var VCharSet: String); virtual;
|
||
|
function GetAttachmentEncoding: string;
|
||
|
function GetInReplyTo: String;
|
||
|
function GetUseNowForDate: Boolean;
|
||
|
function GetFrom: TIdEmailAddressItem;
|
||
|
procedure SetAttachmentEncoding(const AValue: string);
|
||
|
procedure SetAttachmentTempDirectory(const Value: string);
|
||
|
procedure SetBccList(const AValue: TIdEmailAddressList);
|
||
|
procedure SetBody(const AValue: TStrings);
|
||
|
procedure SetCCList(const AValue: TIdEmailAddressList);
|
||
|
procedure SetContentType(const AValue: String);
|
||
|
procedure SetEncoding(const AValue: TIdMessageEncoding);
|
||
|
procedure SetExtraHeaders(const AValue: TIdHeaderList);
|
||
|
procedure SetFrom(const AValue: TIdEmailAddressItem);
|
||
|
procedure SetFromList(const AValue: TIdEmailAddressList);
|
||
|
procedure SetHeaders(const AValue: TIdHeaderList);
|
||
|
procedure SetInReplyTo(const AValue : String);
|
||
|
procedure SetMsgID(const AValue : String);
|
||
|
procedure SetNewsGroups(const AValue: TStrings);
|
||
|
procedure SetReceiptRecipient(const AValue: TIdEmailAddressItem);
|
||
|
procedure SetRecipients(const AValue: TIdEmailAddressList);
|
||
|
procedure SetReplyTo(const AValue: TIdEmailAddressList);
|
||
|
procedure SetSender(const AValue: TIdEmailAddressItem);
|
||
|
procedure SetUseNowForDate(const AValue: Boolean);
|
||
|
procedure InitComponent; override;
|
||
|
public
|
||
|
destructor Destroy; override;
|
||
|
|
||
|
procedure AddHeader(const AValue: string);
|
||
|
procedure Clear; virtual;
|
||
|
procedure ClearBody;
|
||
|
procedure ClearHeader;
|
||
|
procedure GenerateHeader;
|
||
|
procedure InitializeISO(var VHeaderEncoding: Char; var VCharSet: String);
|
||
|
function IsBodyEncodingRequired: Boolean;
|
||
|
function IsBodyEmpty: Boolean;
|
||
|
|
||
|
procedure LoadFromFile(const AFileName: string; const AHeadersOnly: Boolean = False);
|
||
|
procedure LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
|
||
|
|
||
|
procedure ProcessHeaders;
|
||
|
|
||
|
procedure SaveToFile(const AFileName : string; const AHeadersOnly: Boolean = False);
|
||
|
procedure SaveToStream(AStream: TStream; const AHeadersOnly: Boolean = False);
|
||
|
|
||
|
procedure DoCreateAttachment(const AHeaders: TStrings; var VAttachment: TIdAttachment); virtual;
|
||
|
//
|
||
|
property Flags: TIdMessageFlagsSet read FFlags write FFlags;
|
||
|
property IsEncoded : Boolean read FIsEncoded write FIsEncoded;
|
||
|
property MsgId: string read FMsgId write SetMsgID;
|
||
|
property Headers: TIdHeaderList read FHeaders write SetHeaders;
|
||
|
property MessageParts: TIdMessageParts read FMessageParts;
|
||
|
property MIMEBoundary: TIdMIMEBoundary read FMIMEBoundary;
|
||
|
property UID: String read FUID write FUID;
|
||
|
property IsMsgSinglePartMime: Boolean read FIsMsgSinglePartMime write FIsMsgSinglePartMime;
|
||
|
published
|
||
|
//TODO: Make a property editor which drops down the registered coder types
|
||
|
property AttachmentEncoding: string read GetAttachmentEncoding write SetAttachmentEncoding;
|
||
|
property Body: TStrings read FBody write SetBody;
|
||
|
property BccList: TIdEmailAddressList read FBccList write SetBccList;
|
||
|
property CharSet: string read FCharSet write FCharSet;
|
||
|
property CCList: TIdEmailAddressList read FCcList write SetCcList;
|
||
|
property ContentType: string read FContentType write SetContentType;
|
||
|
property ContentTransferEncoding: string read FContentTransferEncoding
|
||
|
write FContentTransferEncoding;
|
||
|
property ContentDisposition: string read FContentDisposition write FContentDisposition;
|
||
|
property Date: TDateTime read FDate write FDate;
|
||
|
//
|
||
|
property Encoding: TIdMessageEncoding read FEncoding write SetEncoding;
|
||
|
property ExtraHeaders: TIdHeaderList read FExtraHeaders write SetExtraHeaders;
|
||
|
property FromList: TIdEmailAddressList read FFromList write SetFromList;
|
||
|
property From: TIdEmailAddressItem read GetFrom write SetFrom;
|
||
|
property NewsGroups: TStrings read FNewsGroups write SetNewsGroups;
|
||
|
property NoEncode: Boolean read FNoEncode write FNoEncode default ID_MSG_NODECODE;
|
||
|
property NoDecode: Boolean read FNoDecode write FNoDecode default ID_MSG_NODECODE;
|
||
|
property Organization: string read FOrganization write FOrganization;
|
||
|
property Priority: TIdMessagePriority read FPriority write FPriority default ID_MSG_PRIORITY;
|
||
|
property ReceiptRecipient: TIdEmailAddressItem read FReceiptRecipient write SetReceiptRecipient;
|
||
|
property Recipients: TIdEmailAddressList read FRecipients write SetRecipients;
|
||
|
property References: string read FReferences write FReferences;
|
||
|
property InReplyTo : String read GetInReplyTo write SetInReplyTo;
|
||
|
property ReplyTo: TIdEmailAddressList read FReplyTo write SetReplyTo;
|
||
|
property Subject: string read FSubject write FSubject;
|
||
|
property Sender: TIdEmailAddressItem read FSender write SetSender;
|
||
|
property UseNowForDate: Boolean read GetUseNowForDate write SetUseNowForDate default ID_MSG_USESNOWFORDATE;
|
||
|
property LastGeneratedHeaders: TIdHeaderList read FLastGeneratedHeaders;
|
||
|
property ConvertPreamble: Boolean read FConvertPreamble write FConvertPreamble;
|
||
|
property ExceptionOnBlockedAttachments: Boolean read FExceptionOnBlockedAttachments write FExceptionOnBlockedAttachments default False;
|
||
|
property AttachmentTempDirectory: string read FAttachmentTempDirectory write SetAttachmentTempDirectory;
|
||
|
|
||
|
// Events
|
||
|
property OnInitializeISO: TIdInitializeIsoEvent read FOnInitializeISO write FOnInitializeISO;
|
||
|
property OnCreateAttachment: TIdCreateAttachmentEvent read FOnCreateAttachment write FOnCreateAttachment;
|
||
|
End;
|
||
|
|
||
|
TIdMessageEvent = procedure(ASender : TComponent; var AMsg : TIdMessage) of object;
|
||
|
|
||
|
EIdTextInvalidCount = class(EIdMessageException);
|
||
|
|
||
|
// 2001-Oct-29 Don Siders
|
||
|
EIdMessageCannotLoad = class(EIdMessageException);
|
||
|
|
||
|
const
|
||
|
MessageFlags : array [mfAnswered..mfRecent] of String =
|
||
|
( '\Answered', {Do not Localize} //Message has been answered.
|
||
|
'\Flagged', {Do not Localize} //Message is "flagged" for urgent/special attention.
|
||
|
'\Deleted', {Do not Localize} //Message is "deleted" for removal by later EXPUNGE.
|
||
|
'\Draft', {Do not Localize} //Message has not completed composition (marked as a draft).
|
||
|
'\Seen', {Do not Localize} //Message has been read.
|
||
|
'\Recent' ); {Do not Localize} //Message is "recently" arrived in this mailbox.
|
||
|
|
||
|
INREPLYTO = 'In-Reply-To'; {Do not localize}
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
//facilitate inlining only.
|
||
|
{$IFDEF DOTNET}
|
||
|
{$IFDEF USE_INLINE}
|
||
|
System.IO,
|
||
|
{$ENDIF}
|
||
|
{$ENDIF}
|
||
|
IdIOHandlerStream, IdGlobal,
|
||
|
IdMessageCoderMIME, // Here so the 'MIME' in create will always suceed
|
||
|
IdCharSets, IdGlobalProtocols, IdMessageCoder, IdResourceStringsProtocols,
|
||
|
IdMessageClient, IdAttachmentFile,
|
||
|
IdText, SysUtils;
|
||
|
|
||
|
const
|
||
|
cPriorityStrs: array[TIdMessagePriority] of string = ('urgent', 'urgent', 'normal', 'non-urgent', 'non-urgent');
|
||
|
cImportanceStrs: array[TIdMessagePriority] of string = ('high', 'high', 'normal', 'low', 'low');
|
||
|
|
||
|
{ TIdMIMEBoundary }
|
||
|
|
||
|
procedure TIdMIMEBoundary.Clear;
|
||
|
begin
|
||
|
FBoundaryList.Clear;
|
||
|
FParentPartList.Clear;
|
||
|
end;
|
||
|
|
||
|
function TIdMIMEBoundary.Count: integer;
|
||
|
begin
|
||
|
Result := FBoundaryList.Count;
|
||
|
end;
|
||
|
|
||
|
constructor TIdMIMEBoundary.Create;
|
||
|
begin
|
||
|
inherited;
|
||
|
FBoundaryList := TStringList.Create;
|
||
|
FParentPartList := TStringList.Create;
|
||
|
end;
|
||
|
|
||
|
destructor TIdMIMEBoundary.Destroy;
|
||
|
begin
|
||
|
FreeAndNil(FBoundaryList);
|
||
|
FreeAndNil(FParentPartList);
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
function TIdMIMEBoundary.GetBoundary: string;
|
||
|
begin
|
||
|
if FBoundaryList.Count > 0 then begin
|
||
|
Result := FBoundaryList.Strings[0];
|
||
|
end else begin
|
||
|
Result := '';
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdMIMEBoundary.GetParentPart: integer;
|
||
|
begin
|
||
|
if FParentPartList.Count > 0 then begin
|
||
|
Result := IndyStrToInt(FParentPartList.Strings[0]);
|
||
|
end else begin
|
||
|
Result := -1;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMIMEBoundary.Pop;
|
||
|
begin
|
||
|
if FBoundaryList.Count > 0 then begin
|
||
|
FBoundaryList.Delete(0);
|
||
|
end;
|
||
|
if FParentPartList.Count > 0 then begin
|
||
|
FParentPartList.Delete(0);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMIMEBoundary.Push(ABoundary: string; AParentPart: integer);
|
||
|
begin
|
||
|
{CC: Changed implementation to a simple stack}
|
||
|
FBoundaryList.Insert(0, ABoundary);
|
||
|
FParentPartList.Insert(0, IntToStr(AParentPart));
|
||
|
end;
|
||
|
|
||
|
{ TIdMessage }
|
||
|
|
||
|
procedure TIdMessage.AddHeader(const AValue: string);
|
||
|
begin
|
||
|
FHeaders.Add(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.Clear;
|
||
|
begin
|
||
|
ClearHeader;
|
||
|
ClearBody;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.ClearBody;
|
||
|
begin
|
||
|
MessageParts.Clear;
|
||
|
Body.Clear;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.ClearHeader;
|
||
|
begin
|
||
|
CcList.Clear;
|
||
|
BccList.Clear;
|
||
|
Date := 0;
|
||
|
FromList.Clear;
|
||
|
NewsGroups.Clear;
|
||
|
Organization := '';
|
||
|
References := '';
|
||
|
ReplyTo.Clear;
|
||
|
Subject := '';
|
||
|
Recipients.Clear;
|
||
|
Priority := ID_MSG_PRIORITY;
|
||
|
ReceiptRecipient.Text := '';
|
||
|
FContentType := '';
|
||
|
FCharSet := '';
|
||
|
ContentTransferEncoding := '';
|
||
|
ContentDisposition := '';
|
||
|
FSender.Text := '';
|
||
|
Headers.Clear;
|
||
|
ExtraHeaders.Clear;
|
||
|
FMIMEBoundary.Clear;
|
||
|
// UseNowForDate := ID_MSG_USENOWFORDATE;
|
||
|
Flags := [];
|
||
|
MsgId := '';
|
||
|
UID := '';
|
||
|
FLastGeneratedHeaders.Clear;
|
||
|
FEncoding := meDefault; {CC3: Changed initial encoding from meMIME to meDefault}
|
||
|
FConvertPreamble := True; {By default, in MIME, we convert the preamble text to the 1st TIdText part}
|
||
|
FSavingToFile := False; {Only set True by SaveToFile}
|
||
|
FIsMsgSinglePartMime := False;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.InitComponent;
|
||
|
begin
|
||
|
inherited;
|
||
|
FBody := TStringList.Create;
|
||
|
TStringList(FBody).Duplicates := dupAccept;
|
||
|
FRecipients := TIdEmailAddressList.Create(Self);
|
||
|
FBccList := TIdEmailAddressList.Create(Self);
|
||
|
FCcList := TIdEmailAddressList.Create(Self);
|
||
|
FMessageParts := TIdMessageParts.Create(Self);
|
||
|
FNewsGroups := TStringList.Create;
|
||
|
FHeaders := TIdHeaderList.Create(QuoteRFC822);
|
||
|
FFromList := TIdEmailAddressList.Create(Self);
|
||
|
FReplyTo := TIdEmailAddressList.Create(Self);
|
||
|
FSender := TIdEmailAddressItem.Create;
|
||
|
FExtraHeaders := TIdHeaderList.Create(QuoteRFC822);
|
||
|
FReceiptRecipient := TIdEmailAddressItem.Create;
|
||
|
NoDecode := ID_MSG_NODECODE;
|
||
|
FMIMEBoundary := TIdMIMEBoundary.Create;
|
||
|
FLastGeneratedHeaders := TIdHeaderList.Create(QuoteRFC822);
|
||
|
Clear;
|
||
|
FEncoding := meDefault;
|
||
|
end;
|
||
|
|
||
|
destructor TIdMessage.Destroy;
|
||
|
begin
|
||
|
FreeAndNil(FBody);
|
||
|
FreeAndNil(FRecipients);
|
||
|
FreeAndNil(FBccList);
|
||
|
FreeAndNil(FCcList);
|
||
|
FreeAndNil(FMessageParts);
|
||
|
FreeAndNil(FNewsGroups);
|
||
|
FreeAndNil(FHeaders);
|
||
|
FreeAndNil(FExtraHeaders);
|
||
|
FreeAndNil(FFromList);
|
||
|
FreeAndNil(FReplyTo);
|
||
|
FreeAndNil(FSender);
|
||
|
FreeAndNil(FReceiptRecipient);
|
||
|
FreeAndNil(FMIMEBoundary);
|
||
|
FreeAndNil(FLastGeneratedHeaders);
|
||
|
inherited Destroy;
|
||
|
end;
|
||
|
|
||
|
function TIdMessage.IsBodyEmpty: Boolean;
|
||
|
//Determine if there really is anything in the body
|
||
|
var
|
||
|
LN: integer;
|
||
|
LOrd: integer;
|
||
|
begin
|
||
|
Result := False;
|
||
|
for LN := 1 to Length(Body.Text) do begin
|
||
|
LOrd := Ord(Body.Text[LN]);
|
||
|
if ((LOrd <> 13) and (LOrd <> 10) and (LOrd <> 9) and (LOrd <> 32)) then begin
|
||
|
Exit;
|
||
|
end;
|
||
|
end;
|
||
|
Result := True;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.GenerateHeader;
|
||
|
var
|
||
|
ISOCharset: string;
|
||
|
HeaderEncoding: Char;
|
||
|
LN: Integer;
|
||
|
LEncoding, LCharSet, LMIMEBoundary: string;
|
||
|
LDate: TDateTime;
|
||
|
LReceiptRecipient: string;
|
||
|
begin
|
||
|
MessageParts.CountParts;
|
||
|
{CC2: If the encoding is meDefault, the user wants us to pick an encoding mechanism:}
|
||
|
if Encoding = meDefault then begin
|
||
|
if MessageParts.Count = 0 then begin
|
||
|
{If there are no attachments, we want the simplest type, just the headers
|
||
|
followed by the message body: mePlainText does this for us}
|
||
|
Encoding := mePlainText;
|
||
|
end else begin
|
||
|
{If there are any attachments, default to MIME...}
|
||
|
Encoding := meMIME;
|
||
|
end;
|
||
|
end;
|
||
|
for LN := 0 to MessageParts.Count-1 do begin
|
||
|
{Change any encodings we don't know to base64 for MIME and UUE for PlainText...}
|
||
|
LEncoding := MessageParts[LN].ContentTransfer;
|
||
|
if LEncoding <> '' then begin
|
||
|
if Encoding = meMIME then begin
|
||
|
if PosInStrArray(LEncoding, ['7bit', '8bit', 'binary', 'base64', 'quoted-printable', 'binhex40'], False) = -1 then begin {do not localize}
|
||
|
MessageParts[LN].ContentTransfer := 'base64'; {do not localize}
|
||
|
end;
|
||
|
end
|
||
|
else if PosInStrArray(LEncoding, ['UUE', 'XXE'], False) = -1 then begin {do not localize}
|
||
|
//mePlainText
|
||
|
MessageParts[LN].ContentTransfer := 'UUE'; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{RLebeau: should we validate the TIdMessage.ContentTransferEncoding property as well?}
|
||
|
|
||
|
{CC2: We dont support attachments in an encoded body.
|
||
|
Change it to a supported combination...}
|
||
|
if MessageParts.Count > 0 then begin
|
||
|
if (ContentTransferEncoding <> '') and
|
||
|
(PosInStrArray(ContentTransferEncoding, ['7bit', '8bit', 'binary'], False) = -1) then begin {do not localize}
|
||
|
ContentTransferEncoding := '';
|
||
|
end;
|
||
|
end;
|
||
|
if Encoding = meMIME then begin
|
||
|
//HH: Generate Boundary here so we know it in the headers and body
|
||
|
//######### SET UP THE BOUNDARY STACK ########
|
||
|
//RLebeau: Moved this logic up from SendBody to here, where it fits better...
|
||
|
MIMEBoundary.Clear;
|
||
|
LMIMEBoundary := TIdMIMEBoundaryStrings.GenerateBoundary;
|
||
|
MIMEBoundary.Push(LMIMEBoundary, -1); //-1 is "top level"
|
||
|
//CC: Moved this logic up from SendBody to here, where it fits better...
|
||
|
if Length(ContentType) = 0 then begin
|
||
|
//User has omitted ContentType. We have to guess here, it is impossible
|
||
|
//to determine without having procesed the parts.
|
||
|
//See if it is multipart/alternative...
|
||
|
if MessageParts.TextPartCount > 1 then begin
|
||
|
if MessageParts.AttachmentCount > 0 then begin
|
||
|
ContentType := 'multipart/mixed'; {do not localize}
|
||
|
end else begin
|
||
|
ContentType := 'multipart/alternative'; {do not localize}
|
||
|
end;
|
||
|
end else
|
||
|
begin
|
||
|
//Just one (or 0?) text part.
|
||
|
if MessageParts.AttachmentCount > 0 then begin
|
||
|
ContentType := 'multipart/mixed'; {do not localize}
|
||
|
end else begin
|
||
|
ContentType := 'text/plain'; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
TIdMessageEncoderInfo(MessageParts.MessageEncoderInfo).InitializeHeaders(Self);
|
||
|
end;
|
||
|
|
||
|
InitializeISO(HeaderEncoding, ISOCharSet);
|
||
|
FLastGeneratedHeaders.Assign(FHeaders);
|
||
|
FIsMsgSinglePartMime := (Encoding = meMIME) and (MessageParts.Count = 1) and IsBodyEmpty;
|
||
|
|
||
|
// TODO: when STRING_IS_ANSI is defined, provide a way for the user to specify the AnsiString encoding for header values...
|
||
|
|
||
|
{CC: If From has no Name field, use the Address field as the Name field by setting last param to True (for SA)...}
|
||
|
FLastGeneratedHeaders.Values['From'] := EncodeAddress(FromList, HeaderEncoding, ISOCharSet, True); {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Subject'] := EncodeHeader(Subject, '', HeaderEncoding, ISOCharSet); {do not localize}
|
||
|
FLastGeneratedHeaders.Values['To'] := EncodeAddress(Recipients, HeaderEncoding, ISOCharSet); {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Cc'] := EncodeAddress(CCList, HeaderEncoding, ISOCharSet); {do not localize}
|
||
|
{CC: SaveToFile sets FSavingToFile to True so that BCC names are saved
|
||
|
when saving to file and omitted otherwise (as required by SMTP)...}
|
||
|
if not FSavingToFile then begin
|
||
|
FLastGeneratedHeaders.Values['Bcc'] := ''; {do not localize}
|
||
|
end else begin
|
||
|
FLastGeneratedHeaders.Values['Bcc'] := EncodeAddress(BCCList, HeaderEncoding, ISOCharSet); {do not localize}
|
||
|
end;
|
||
|
FLastGeneratedHeaders.Values['Newsgroups'] := NewsGroups.CommaText; {do not localize}
|
||
|
|
||
|
if Encoding = meMIME then
|
||
|
begin
|
||
|
if IsMsgSinglePartMime then begin
|
||
|
{This is a single-part MIME: the part may be a text part or an attachment.
|
||
|
The relevant headers need to be taken from MessageParts[0]. The problem,
|
||
|
however, is that we have not yet processed MessageParts[0] yet, so we do
|
||
|
not have its properties or header content properly set up. So we will
|
||
|
let the processing of MessageParts[0] append its headers to the message
|
||
|
headers, i.e. DON'T generate Content-Type or Content-Transfer-Encoding
|
||
|
headers here.}
|
||
|
FLastGeneratedHeaders.Values['MIME-Version'] := '1.0'; {do not localize}
|
||
|
|
||
|
{RLebeau: need to wipe out the following headers if they were present,
|
||
|
otherwise MessageParts[0] will duplicate them instead of replacing them.
|
||
|
This is because LastGeneratedHeaders is sent before MessageParts[0] is
|
||
|
processed.}
|
||
|
FLastGeneratedHeaders.Values['Content-Type'] := '';
|
||
|
FLastGeneratedHeaders.Values['Content-Transfer-Encoding'] := '';
|
||
|
FLastGeneratedHeaders.Values['Content-Disposition'] := '';
|
||
|
end else begin
|
||
|
if FContentType <> '' then begin
|
||
|
LCharSet := FCharSet;
|
||
|
if (LCharSet = '') and IsHeaderMediaType(FContentType, 'text') then begin {do not localize}
|
||
|
LCharSet := 'us-ascii'; {do not localize}
|
||
|
end;
|
||
|
FLastGeneratedHeaders.Values['Content-Type'] := FContentType; {do not localize}
|
||
|
FLastGeneratedHeaders.Params['Content-Type', 'charset'] := LCharSet; {do not localize}
|
||
|
if (MessageParts.Count > 0) and (LMIMEBoundary <> '') then begin
|
||
|
FLastGeneratedHeaders.Params['Content-Type', 'boundary'] := LMIMEBoundary; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
{CC2: We may have MIME with no parts if ConvertPreamble is True}
|
||
|
FLastGeneratedHeaders.Values['MIME-Version'] := '1.0'; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Content-Transfer-Encoding'] := ContentTransferEncoding; {do not localize}
|
||
|
end;
|
||
|
end else begin
|
||
|
//CC: non-MIME can have ContentTransferEncoding of base64, quoted-printable...
|
||
|
LCharSet := FCharSet;
|
||
|
if (LCharSet = '') and IsHeaderMediaType(FContentType, 'text') then begin {do not localize}
|
||
|
LCharSet := 'us-ascii'; {do not localize}
|
||
|
end;
|
||
|
FLastGeneratedHeaders.Values['Content-Type'] := FContentType; {do not localize}
|
||
|
FLastGeneratedHeaders.Params['Content-Type', 'charset'] := LCharSet; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Content-Transfer-Encoding'] := ContentTransferEncoding; {do not localize}
|
||
|
end;
|
||
|
FLastGeneratedHeaders.Values['Sender'] := Sender.Text; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Reply-To'] := EncodeAddress(ReplyTo, HeaderEncoding, ISOCharSet); {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Organization'] := EncodeHeader(Organization, '', HeaderEncoding, ISOCharSet); {do not localize}
|
||
|
|
||
|
LReceiptRecipient := EncodeAddressItem(ReceiptRecipient, HeaderEncoding, ISOCharSet);
|
||
|
FLastGeneratedHeaders.Values['Disposition-Notification-To'] := LReceiptRecipient; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Return-Receipt-To'] := LReceiptRecipient; {do not localize}
|
||
|
|
||
|
FLastGeneratedHeaders.Values['References'] := References; {do not localize}
|
||
|
|
||
|
if UseNowForDate then begin
|
||
|
LDate := Now;
|
||
|
end else begin
|
||
|
LDate := Self.Date;
|
||
|
end;
|
||
|
FLastGeneratedHeaders.Values['Date'] := LocalDateTimeToGMT(LDate); {do not localize}
|
||
|
|
||
|
// S.G. 27/1/2003: Only issue X-Priority header if priority <> mpNormal (for stoopid spam filters)
|
||
|
// RLebeau 2/2/2014: add a new Importance property
|
||
|
if Priority <> mpNormal then begin
|
||
|
FLastGeneratedHeaders.Values['Priority'] := cPriorityStrs[Priority]; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['X-Priority'] := IntToStr(Ord(Priority) + 1); {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Importance'] := cImportanceStrs[Priority]; {do not localize}
|
||
|
end else begin
|
||
|
FLastGeneratedHeaders.Values['Priority'] := ''; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['X-Priority'] := ''; {do not localize}
|
||
|
FLastGeneratedHeaders.Values['Importance'] := ''; {do not localize}
|
||
|
end;
|
||
|
|
||
|
{CC: SaveToFile sets FSavingToFile to True so that Message IDs
|
||
|
are saved when saving to file and omitted otherwise ...}
|
||
|
if not FSavingToFile then begin
|
||
|
FLastGeneratedHeaders.Values['Message-Id'] := '';
|
||
|
end else begin
|
||
|
FLastGeneratedHeaders.Values['Message-Id'] := MsgId;
|
||
|
end;
|
||
|
|
||
|
// Add extra headers created by UA - allows duplicates
|
||
|
if (FExtraHeaders.Count > 0) then begin
|
||
|
FLastGeneratedHeaders.AddStrings(FExtraHeaders);
|
||
|
end;
|
||
|
|
||
|
{Generate In-Reply-To if at all possible to pacify SA. Do this after FExtraHeaders
|
||
|
added in case there is a message-ID present as an extra header.}
|
||
|
if InReplyTo = '' then begin
|
||
|
if FLastGeneratedHeaders.Values['Message-ID'] <> '' then begin {do not localize}
|
||
|
FLastGeneratedHeaders.Values['In-Reply-To'] := FLastGeneratedHeaders.Values['Message-ID']; {do not localize}
|
||
|
end else begin
|
||
|
{CC: The following was originally present, but it so wrong that it has to go!
|
||
|
Values['In-Reply-To'] := Subject; {do not localize}
|
||
|
end;
|
||
|
end else begin
|
||
|
FLastGeneratedHeaders.Values['In-Reply-To'] := InReplyTo; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.ProcessHeaders;
|
||
|
var
|
||
|
LBoundary: string;
|
||
|
LMIMEVersion: string;
|
||
|
|
||
|
// Some mailers send priority as text, number or combination of both
|
||
|
function GetMsgPriority(APriority: string): TIdMessagePriority;
|
||
|
var
|
||
|
s: string;
|
||
|
Num: integer;
|
||
|
begin
|
||
|
APriority := LowerCase(APriority);
|
||
|
// TODO: use PostInStrArray() instead of IndyPos()
|
||
|
// This is for Pegasus / X-MSMail-Priority / Importance headers
|
||
|
if (IndyPos('non-urgent', APriority) <> 0) or {do not localize}
|
||
|
(IndyPos('low', APriority) <> 0) then {do not localize}
|
||
|
begin
|
||
|
Result := mpLowest;
|
||
|
// Although a matter of choice, IMO mpLowest is better choice than mpLow,
|
||
|
// various examples on the net also use 1 as urgent and 5 as non-urgent
|
||
|
end
|
||
|
else if (IndyPos('urgent', APriority) <> 0) or {do not localize}
|
||
|
(IndyPos('high', APriority) <> 0) then {do not localize}
|
||
|
begin
|
||
|
Result := mpHighest;
|
||
|
// Although a matter of choice, IMO mpHighest is better choice than mpHigh,
|
||
|
// various examples on the net also use 1 as urgent and 5 as non-urgent
|
||
|
end else
|
||
|
begin
|
||
|
s := Trim(APriority);
|
||
|
Num := IndyStrToInt(Fetch(s, ' '), 3); {do not localize}
|
||
|
if (Num < 1) or (Num > 5) then begin
|
||
|
Num := 3;
|
||
|
end;
|
||
|
Result := TIdMessagePriority(Num - 1);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
begin
|
||
|
// RLebeau: per RFC 2045 Section 5.2:
|
||
|
//
|
||
|
// Default RFC 822 messages without a MIME Content-Type header are taken
|
||
|
// by this protocol to be plain text in the US-ASCII character set,
|
||
|
// which can be explicitly specified as:
|
||
|
//
|
||
|
// Content-type: text/plain; charset=us-ascii
|
||
|
//
|
||
|
// This default is assumed if no Content-Type header field is specified.
|
||
|
// It is also recommend that this default be assumed when a
|
||
|
// syntactically invalid Content-Type header field is encountered. In
|
||
|
// the presence of a MIME-Version header field and the absence of any
|
||
|
// Content-Type header field, a receiving User Agent can also assume
|
||
|
// that plain US-ASCII text was the sender's intent. Plain US-ASCII
|
||
|
// text may still be assumed in the absence of a MIME-Version or the
|
||
|
// presence of an syntactically invalid Content-Type header field, but
|
||
|
// the sender's intent might have been otherwise.
|
||
|
|
||
|
FContentType := Headers.Values['Content-Type']; {do not localize}
|
||
|
if FContentType = '' then begin
|
||
|
FContentType := 'text/plain'; {do not localize}
|
||
|
FCharSet := 'us-ascii'; {do not localize}
|
||
|
end else begin
|
||
|
FContentType := RemoveHeaderEntry(FContentType, 'charset', FCharSet, QuoteMIME); {do not localize}
|
||
|
if (FCharSet = '') and IsHeaderMediaType(FContentType, 'text') then begin {do not localize}
|
||
|
FCharSet := 'us-ascii'; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
ContentTransferEncoding := Headers.Values['Content-Transfer-Encoding']; {do not localize}
|
||
|
ContentDisposition := Headers.Values['Content-Disposition']; {do not localize}
|
||
|
Subject := DecodeHeader(Headers.Values['Subject']); {do not localize}
|
||
|
DecodeAddresses(Headers.Values['From'], FromList); {do not localize}
|
||
|
MsgId := Headers.Values['Message-Id']; {do not localize}
|
||
|
CommaSeparatedToStringList(Newsgroups, Headers.Values['Newsgroups']); {do not localize}
|
||
|
DecodeAddresses(Headers.Values['To'], Recipients); {do not localize}
|
||
|
DecodeAddresses(Headers.Values['Cc'], CCList); {do not localize}
|
||
|
{CC2: Added support for BCCList...}
|
||
|
DecodeAddresses(Headers.Values['Bcc'], BCCList); {do not localize}
|
||
|
Organization := Headers.Values['Organization']; {do not localize}
|
||
|
InReplyTo := Headers.Values['In-Reply-To']; {do not localize}
|
||
|
|
||
|
ReceiptRecipient.Text := Headers.Values['Disposition-Notification-To']; {do not localize}
|
||
|
if Length(ReceiptRecipient.Text) = 0 then begin
|
||
|
ReceiptRecipient.Text := Headers.Values['Return-Receipt-To']; {do not localize}
|
||
|
end;
|
||
|
|
||
|
References := Headers.Values['References']; {do not localize}
|
||
|
|
||
|
DecodeAddresses(Headers.Values['Reply-To'], ReplyTo); {do not localize}
|
||
|
|
||
|
Date := GMTToLocalDateTime(Headers.Values['Date']); {do not localize}
|
||
|
Sender.Text := Headers.Values['Sender']; {do not localize}
|
||
|
|
||
|
// RLebeau 2/2/2014: add a new Importance property
|
||
|
if Length(Headers.Values['X-Priority']) > 0 then begin {do not localize}
|
||
|
// Examine X-Priority first - to get better resolution if possible and because it is the most common
|
||
|
Priority := GetMsgPriority(Headers.Values['X-Priority']); {do not localize}
|
||
|
end
|
||
|
else if Length(Headers.Values['Priority']) > 0 then begin {do not localize}
|
||
|
// Which header should be here is matter of a bit of research, it might be that Importance might be checked first
|
||
|
Priority := GetMsgPriority(Headers.Values['Priority']) {do not localize}
|
||
|
end
|
||
|
else if Length(Headers.Values['Importance']) > 0 then begin {do not localize}
|
||
|
// Check Importance or Priority
|
||
|
Priority := GetMsgPriority(Headers.Values['Importance']) {do not localize}
|
||
|
end
|
||
|
else if Length(Headers.Values['X-MSMail-Priority']) > 0 then begin {do not localize}
|
||
|
// This is the least common header (or at least should be) so can be checked last
|
||
|
Priority := GetMsgPriority(Headers.Values['X-MSMail-Priority']) {do not localize}
|
||
|
end
|
||
|
else begin
|
||
|
Priority := mpNormal;
|
||
|
end;
|
||
|
|
||
|
{Note that the following code ensures MIMEBoundary.Count is 0 for single-part MIME messages...}
|
||
|
FContentType := RemoveHeaderEntry(FContentType, 'boundary', LBoundary, QuoteMIME); {do not localize}
|
||
|
if LBoundary <> '' then begin
|
||
|
MIMEBoundary.Push(LBoundary, -1);
|
||
|
end;
|
||
|
|
||
|
{CC2: Set MESSAGE_LEVEL "encoding" (really the format or layout)}
|
||
|
LMIMEVersion := Headers.Values['MIME-Version']; {do not localize}
|
||
|
if LMIMEVersion = '' then begin
|
||
|
Encoding := mePlainText;
|
||
|
end else begin
|
||
|
// TODO: this should be true if a MIME boundary is present.
|
||
|
// The MIME version is optional...
|
||
|
Encoding := meMIME;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetBccList(const AValue: TIdEmailAddressList);
|
||
|
begin
|
||
|
FBccList.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetBody(const AValue: TStrings);
|
||
|
begin
|
||
|
FBody.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetCCList(const AValue: TIdEmailAddressList);
|
||
|
begin
|
||
|
FCcList.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetContentType(const AValue: String);
|
||
|
var
|
||
|
LCharSet: String;
|
||
|
begin
|
||
|
// RLebeau: per RFC 2045 Section 5.2:
|
||
|
//
|
||
|
// Default RFC 822 messages without a MIME Content-Type header are taken
|
||
|
// by this protocol to be plain text in the US-ASCII character set,
|
||
|
// which can be explicitly specified as:
|
||
|
//
|
||
|
// Content-type: text/plain; charset=us-ascii
|
||
|
//
|
||
|
// This default is assumed if no Content-Type header field is specified.
|
||
|
// It is also recommend that this default be assumed when a
|
||
|
// syntactically invalid Content-Type header field is encountered. In
|
||
|
// the presence of a MIME-Version header field and the absence of any
|
||
|
// Content-Type header field, a receiving User Agent can also assume
|
||
|
// that plain US-ASCII text was the sender's intent. Plain US-ASCII
|
||
|
// text may still be assumed in the absence of a MIME-Version or the
|
||
|
// presence of an syntactically invalid Content-Type header field, but
|
||
|
// the sender's intent might have been otherwise.
|
||
|
|
||
|
if AValue <> '' then
|
||
|
begin
|
||
|
FContentType := RemoveHeaderEntry(AValue, 'charset', LCharSet, QuoteMIME); {do not localize}
|
||
|
|
||
|
{RLebeau: the ContentType property is streamed after the CharSet property,
|
||
|
so do not overwrite it during streaming}
|
||
|
if csReading in ComponentState then begin
|
||
|
Exit;
|
||
|
end;
|
||
|
|
||
|
if (LCharSet = '') and IsHeaderMediaType(FContentType, 'text') then begin {do not localize}
|
||
|
LCharSet := 'us-ascii'; {do not localize}
|
||
|
end;
|
||
|
|
||
|
{RLebeau: override the current CharSet only if the header specifies a new value}
|
||
|
if LCharSet <> '' then begin
|
||
|
FCharSet := LCharSet;
|
||
|
end;
|
||
|
end else
|
||
|
begin
|
||
|
FContentType := 'text/plain'; {do not localize}
|
||
|
|
||
|
{RLebeau: the ContentType property is streamed after the CharSet property,
|
||
|
so do not overwrite it during streaming}
|
||
|
if not (csReading in ComponentState) then begin
|
||
|
FCharSet := 'us-ascii'; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetExtraHeaders(const AValue: TIdHeaderList);
|
||
|
begin
|
||
|
FExtraHeaders.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetFrom(const AValue: TIdEmailAddressItem);
|
||
|
begin
|
||
|
GetFrom.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
function TIdMessage.GetFrom: TIdEmailAddressItem;
|
||
|
begin
|
||
|
if FFromList.Count = 0 then begin
|
||
|
FFromList.Add;
|
||
|
end;
|
||
|
Result := FFromList[0];
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetFromList(const AValue: TIdEmailAddressList);
|
||
|
begin
|
||
|
FFromList.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetHeaders(const AValue: TIdHeaderList);
|
||
|
begin
|
||
|
FHeaders.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetNewsGroups(const AValue: TStrings);
|
||
|
begin
|
||
|
FNewsgroups.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetReceiptRecipient(const AValue: TIdEmailAddressItem);
|
||
|
begin
|
||
|
FReceiptRecipient.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetRecipients(const AValue: TIdEmailAddressList);
|
||
|
begin
|
||
|
FRecipients.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetReplyTo(const AValue: TIdEmailAddressList);
|
||
|
begin
|
||
|
FReplyTo.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetSender(const AValue: TIdEmailAddressItem);
|
||
|
begin
|
||
|
FSender.Assign(AValue);
|
||
|
end;
|
||
|
|
||
|
function TIdMessage.GetUseNowForDate: Boolean;
|
||
|
begin
|
||
|
Result := (FDate = 0);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetUseNowForDate(const AValue: Boolean);
|
||
|
begin
|
||
|
if GetUseNowForDate <> AValue then begin
|
||
|
if AValue then begin
|
||
|
FDate := 0;
|
||
|
end else begin
|
||
|
FDate := Now;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetAttachmentEncoding(const AValue: string);
|
||
|
begin
|
||
|
MessageParts.AttachmentEncoding := AValue;
|
||
|
end;
|
||
|
|
||
|
function TIdMessage.GetAttachmentEncoding: string;
|
||
|
begin
|
||
|
Result := MessageParts.AttachmentEncoding;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetEncoding(const AValue: TIdMessageEncoding);
|
||
|
begin
|
||
|
FEncoding := AValue;
|
||
|
if AValue = meMIME then begin
|
||
|
AttachmentEncoding := 'MIME'; {do not localize}
|
||
|
end else begin
|
||
|
//Default to UUE for mePlainText, user can override to XXE by calling
|
||
|
//TIdMessage.AttachmentEncoding := 'XXE';
|
||
|
AttachmentEncoding := 'UUE'; {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.LoadFromFile(const AFileName: string; const AHeadersOnly: Boolean = False);
|
||
|
var
|
||
|
LStream: TIdReadFileExclusiveStream;
|
||
|
begin
|
||
|
if not FileExists(AFilename) then begin
|
||
|
raise EIdMessageCannotLoad.CreateFmt(RSIdMessageCannotLoad, [AFilename]);
|
||
|
end;
|
||
|
LStream := TIdReadFileExclusiveStream.Create(AFilename); try
|
||
|
LoadFromStream(LStream, AHeadersOnly);
|
||
|
finally FreeAndNil(LStream); end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
|
||
|
var
|
||
|
LMsgClient: TIdMessageClient;
|
||
|
begin
|
||
|
// clear message properties, headers before loading
|
||
|
Clear;
|
||
|
LMsgClient := TIdMessageClient.Create;
|
||
|
try
|
||
|
LMsgClient.ProcessMessage(Self, AStream, AHeadersOnly);
|
||
|
finally
|
||
|
LMsgClient.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SaveToFile(const AFileName: string; const AHeadersOnly: Boolean = False);
|
||
|
var
|
||
|
LStream : TFileStream;
|
||
|
begin
|
||
|
LStream := TIdFileCreateStream.Create(AFileName); try
|
||
|
FSavingToFile := True; try
|
||
|
SaveToStream(LStream, AHeadersOnly);
|
||
|
finally FSavingToFile := False; end;
|
||
|
finally FreeAndNil(LStream); end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SaveToStream(AStream: TStream; const AHeadersOnly: Boolean = False);
|
||
|
var
|
||
|
LMsgClient: TIdMessageClient;
|
||
|
LIOHandler: TIdIOHandlerStream;
|
||
|
begin
|
||
|
LMsgClient := TIdMessageClient.Create(nil); try
|
||
|
LIOHandler := TIdIOHandlerStream.Create(nil, nil, AStream); try
|
||
|
LIOHandler.FreeStreams := False;
|
||
|
LMsgClient.IOHandler := LIOHandler;
|
||
|
LMsgClient.SendMsg(Self, AHeadersOnly);
|
||
|
// add the end of message marker when body is included
|
||
|
if not AHeadersOnly then begin
|
||
|
LMsgClient.IOHandler.WriteLn('.'); {do not localize}
|
||
|
end;
|
||
|
finally FreeAndNil(LIOHandler); end;
|
||
|
finally FreeAndNil(LMsgClient); end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.DoInitializeISO(var VHeaderEncoding: Char; var VCharSet: string);
|
||
|
begin
|
||
|
if Assigned(FOnInitializeISO) then begin
|
||
|
FOnInitializeISO(VHeaderEncoding, VCharSet);//APR
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.InitializeISO(var VHeaderEncoding: Char; var VCharSet: String);
|
||
|
var
|
||
|
LDefCharset: TIdCharSet;
|
||
|
begin
|
||
|
// it's not clear when FHeaderEncoding should be Q not B.
|
||
|
// Comments welcome on atozedsoftware.indy.general
|
||
|
|
||
|
LDefCharset := IdGetDefaultCharSet;
|
||
|
|
||
|
case LDefCharset of
|
||
|
idcs_ISO_8859_1:
|
||
|
begin
|
||
|
VHeaderEncoding := 'Q'; { quoted-printable } {Do not Localize}
|
||
|
VCharSet := IdCharsetNames[LDefCharset];
|
||
|
end;
|
||
|
idcs_UNICODE_1_1:
|
||
|
begin
|
||
|
VHeaderEncoding := 'B'; { base64 } {Do not Localize}
|
||
|
VCharSet := IdCharsetNames[idcs_UTF_8];
|
||
|
end;
|
||
|
else
|
||
|
begin
|
||
|
VHeaderEncoding := 'B'; { base64 } {Do not Localize}
|
||
|
VCharSet := IdCharsetNames[LDefCharset];
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
DoInitializeISO(VHeaderEncoding, VCharSet);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.DoCreateAttachment(const AHeaders: TStrings;
|
||
|
var VAttachment: TIdAttachment);
|
||
|
begin
|
||
|
VAttachment := nil;
|
||
|
if Assigned(FOnCreateAttachment) then begin
|
||
|
FOnCreateAttachment(Self, AHeaders, VAttachment);
|
||
|
end;
|
||
|
if VAttachment = nil then begin
|
||
|
VAttachment := TIdAttachmentFile.Create(MessageParts);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdMessage.IsBodyEncodingRequired: Boolean;
|
||
|
var
|
||
|
i,j: Integer;
|
||
|
S: String;
|
||
|
begin
|
||
|
Result := False;//7bit
|
||
|
for i:= 0 to FBody.Count - 1 do begin
|
||
|
S := FBody[i];
|
||
|
for j := 1 to Length(S) do begin
|
||
|
if S[j] > #127 then begin
|
||
|
Result := True;
|
||
|
Exit;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;//
|
||
|
|
||
|
function TIdMessage.GetInReplyTo: String;
|
||
|
begin
|
||
|
Result := EnsureMsgIDBrackets(FInReplyTo);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetInReplyTo(const AValue: String);
|
||
|
begin
|
||
|
FInReplyTo := EnsureMsgIDBrackets(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetMsgID(const AValue: String);
|
||
|
begin
|
||
|
FMsgId := EnsureMsgIDBrackets(AValue);
|
||
|
end;
|
||
|
|
||
|
procedure TIdMessage.SetAttachmentTempDirectory(const Value: string);
|
||
|
begin
|
||
|
if Value <> AttachmentTempDirectory then begin
|
||
|
FAttachmentTempDirectory := IndyExcludeTrailingPathDelimiter(Value);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
end.
|
||
|
|