restemplate/indy/Protocols/IdMessageParts.pas

430 lines
13 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.8 9/30/2004 5:04:20 PM BGooijen
Self was not initialized
Rev 1.7 01/06/2004 00:28:46 CCostelloe
Minor bug fix
Rev 1.6 5/30/04 11:29:36 PM RLebeau
Added OwnerMessage property to TIdMessageParts for use with
TIdMessagePart.ResolveContentType() under Delphi versions prior to v6,
where the TCollection.Owner method does not exist.
Rev 1.5 16/05/2004 18:55:46 CCostelloe
New TIdText/TIdAttachment processing
Rev 1.4 2004.02.03 5:44:06 PM czhower
Name changes
Rev 1.3 10/17/03 12:06:04 PM RLebeau
Updated TIdMessagePart.Assign() to copy all available header values
rather than select ones.
Rev 1.2 10/17/2003 12:43:12 AM DSiders
Added localization comments.
Rev 1.1 26/09/2003 01:07:18 CCostelloe
Added FParentPart, so that nested MIME types
(like multipart/alternative nested in multipart/related and vica-versa)
can be encoded and decoded (when encoding, need to know this so the
correct boundary is emitted) and so the user can properly define which
parts belong to which sections.
Rev 1.0 11/13/2002 07:57:32 AM JPMugaas
24-Sep-2003 Ciaran Costelloe
- Added FParentPart, so that nested MIME types (like multipart/alternative
nested in multipart/related and vica-versa) can be encoded and decoded
(when encoding, need to know this so the correct boundary is emitted)
and so the user can properly define which parts belong to which sections.
2002-08-30 Andrew P.Rybin
- ExtractHeaderSubItem
- virtual methods. Now descendant can add functionality.
Ex: TIdText.GetContentType = GetContentType w/o charset
}
unit IdMessageParts;
interface
{$i IdCompilerDefines.inc}
uses
Classes,
IdHeaderList,
IdExceptionCore,
IdGlobal;
type
TOnGetMessagePartStream = procedure(AStream: TStream) of object;
TIdMessagePartType = (mptText, mptAttachment);
// if you add to this, please also adjust the case statement in
// TIdMessageParts.CountParts;
TIdMessageParts = class;
TIdMessagePart = class(TCollectionItem)
protected
FContentMD5: string;
FCharSet: string;
FEndBoundary: string;
FExtraHeaders: TIdHeaderList;
FFileName: String;
FName: String;
FHeaders: TIdHeaderList;
FIsEncoded: Boolean;
FOnGetMessagePartStream: TOnGetMessagePartStream;
FParentPart: Integer;
//
function GetContentDisposition: string; virtual;
function GetContentType: string; virtual;
function GetContentTransfer: string; virtual;
function GetContentID: string; virtual;
function GetContentLocation: string; virtual;
function GetContentDescription: string; virtual;
function GetMessageParts: TIdMessageParts;
function GetOwnerMessage: TPersistent;
procedure SetContentDisposition(const Value: string); virtual;
procedure SetContentType(const Value: string); virtual;
procedure SetContentTransfer(const Value: string); virtual;
procedure SetExtraHeaders(const Value: TIdHeaderList);
procedure SetContentID(const Value: string); virtual;
procedure SetContentDescription(const Value: string); virtual;
procedure SetContentLocation(const Value: string); virtual;
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function GetCharSet(AHeader: string): String;
function ResolveContentType(AContentType: string): string; //Fixes up ContentType
class function PartType: TIdMessagePartType; virtual;
//
property IsEncoded: Boolean read FIsEncoded;
property MessageParts: TIdMessageParts read GetMessageParts;
property OwnerMessage: TPersistent read GetOwnerMessage;
property OnGetMessagePartStream: TOnGetMessagePartStream read FOnGetMessagePartStream write FOnGetMessagePartStream;
property Headers: TIdHeaderList read FHeaders;
published
property CharSet: string read FCharSet write FCharSet;
property ContentDescription: string read GetContentDescription write SetContentDescription;
property ContentDisposition: string read GetContentDisposition write SetContentDisposition;
property ContentID: string read GetContentID write SetContentID;
property ContentLocation: string read GetContentLocation write SetContentLocation;
property ContentTransfer: string read GetContentTransfer write SetContentTransfer;
property ContentType: string read GetContentType write SetContentType;
property ExtraHeaders: TIdHeaderList read FExtraHeaders write SetExtraHeaders;
property FileName: String read FFileName write FFileName;
property Name: String read FName write FName;
property ParentPart: integer read FParentPart write FParentPart;
end;
TIdMessagePartClass = class of TIdMessagePart;
TIdMessageParts = class(TOwnedCollection)
protected
FAttachmentEncoding: string;
FAttachmentCount: integer;
FMessageEncoderInfo: TObject;
FRelatedPartCount: integer;
FTextPartCount: integer;
//
function GetItem(Index: Integer): TIdMessagePart;
function GetOwnerMessage: TPersistent;
procedure SetAttachmentEncoding(const AValue: string);
procedure SetItem(Index: Integer; const Value: TIdMessagePart);
public
function Add: TIdMessagePart;
procedure CountParts;
constructor Create(AOwner: TPersistent); reintroduce;
//
property AttachmentCount: integer read FAttachmentCount;
property AttachmentEncoding: string read FAttachmentEncoding write SetAttachmentEncoding;
property Items[Index: Integer]: TIdMessagePart read GetItem write SetItem; default;
property MessageEncoderInfo: TObject read FMessageEncoderInfo;
property OwnerMessage: TPersistent read GetOwnerMessage;
property RelatedPartCount: integer read FRelatedPartCount;
property TextPartCount: integer read FTextPartCount;
end;
EIdCanNotCreateMessagePart = class(EIdMessageException);
implementation
uses
IdMessage, IdGlobalProtocols, IdResourceStringsProtocols, IdMessageCoder, IdCoderHeader,
SysUtils;
{ TIdMessagePart }
procedure TIdMessagePart.Assign(Source: TPersistent);
var
mp: TIdMessagePart;
begin
if Source is TIdMessagePart then begin
mp := TIdMessagePart(Source);
// RLebeau 10/17/2003
Headers.Assign(mp.Headers);
ExtraHeaders.Assign(mp.ExtraHeaders);
CharSet := mp.CharSet;
FileName := mp.FileName;
Name := mp.Name;
end else begin
inherited Assign(Source);
end;
end;
constructor TIdMessagePart.Create(Collection: TCollection);
begin
inherited;
if ClassType = TIdMessagePart then begin
raise EIdCanNotCreateMessagePart.Create(RSTIdMessagePartCreate);
end;
FIsEncoded := False;
FHeaders := TIdHeaderList.Create(QuoteRFC822);
FExtraHeaders := TIdHeaderList.Create(QuoteRFC822);
FParentPart := -1;
end;
destructor TIdMessagePart.Destroy;
begin
FHeaders.Free;
FExtraHeaders.Free;
inherited Destroy;
end;
function TIdMessagePart.GetContentDisposition: string;
begin
Result := Headers.Values['Content-Disposition']; {do not localize}
end;
function TIdMessagePart.GetContentID: string;
begin
Result := Headers.Values['Content-ID']; {do not localize}
end;
function TIdMessagePart.GetContentDescription: string;
begin
Result := Headers.Values['Content-Description']; {do not localize}
end;
function TIdMessagePart.GetContentLocation: string;
begin
Result := Headers.Values['Content-Location']; {do not localize}
end;
function TIdMessagePart.GetContentTransfer: string;
begin
Result := Headers.Values['Content-Transfer-Encoding']; {do not localize}
end;
function TIdMessagePart.GetCharSet(AHeader: string): String;
begin
Result := ExtractHeaderSubItem(AHeader, 'charset', QuoteMIME); {do not localize}
end;
function TIdMessagePart.ResolveContentType(AContentType: string): string;
var
LMsg: TIdMessage;
LParts: TIdMessageParts;
begin
//This extracts 'text/plain' from 'text/plain; charset="xyz"; boundary="123"'
//or, if '', it finds the correct default value for MIME messages.
if AContentType <> '' then begin
Result := AContentType;
end else begin
//If it is MIME, then we need to find the correct default...
LParts := MessageParts;
if Assigned(LParts) then begin
LMsg := TIdMessage(LParts.OwnerMessage);
if Assigned(LMsg) and (LMsg.Encoding = meMIME) then begin
//There is an exception if we are a child of multipart/digest...
if ParentPart <> -1 then begin
AContentType := LParts.Items[ParentPart].Headers.Values['Content-Type']; {do not localize}
if IsHeaderMediaType(AContentType, 'multipart/digest') then begin {do not localize}
Result := 'message/rfc822'; {do not localize}
Exit;
end;
end;
//The default type...
Result := 'text/plain'; {do not localize}
Exit;
end;
end;
Result := ''; //Default for non-MIME messages
end;
end;
function TIdMessagePart.GetContentType: string;
begin
Result := Headers.Values['Content-Type']; {do not localize}
end;
function TIdMessagePart.GetMessageParts: TIdMessageParts;
begin
if Collection is TIdMessageParts then begin
Result := TIdMessageParts(Collection);
end else begin
Result := nil;
end;
end;
function TIdMessagePart.GetOwnerMessage: TPersistent;
var
LParts: TIdMessageParts;
begin
LParts := MessageParts;
if Assigned(LParts) then begin
Result := LParts.OwnerMessage;
end else begin
Result := nil;
end;
end;
class function TIdMessagePart.PartType: TIdMessagePartType;
begin
Result := mptAttachment;
end;
procedure TIdMessagePart.SetContentID(const Value: string);
begin
Headers.Values['Content-ID'] := Value; {do not localize}
end;
procedure TIdMessagePart.SetContentDescription(const Value: string);
begin
Headers.Values['Content-Description'] := Value; {do not localize}
end;
procedure TIdMessagePart.SetContentDisposition(const Value: string);
var
LFileName: string;
begin
Headers.Values['Content-Disposition'] := RemoveHeaderEntry(Value, 'filename', LFileName, QuoteMIME); {do not localize}
{RLebeau: override the current value only if the header specifies a new one}
if LFileName <> '' then begin
LFileName := DecodeHeader(LFileName);
end;
if LFileName <> '' then begin
FFileName := LFileName;
end;
end;
procedure TIdMessagePart.SetContentLocation(const Value: string);
begin
Headers.Values['Content-Location'] := Value; {do not localize}
end;
procedure TIdMessagePart.SetContentTransfer(const Value: string);
begin
Headers.Values['Content-Transfer-Encoding'] := Value; {do not localize}
end;
procedure TIdMessagePart.SetContentType(const Value: string);
var
LTmp, LCharSet, LName: string;
begin
LTmp := RemoveHeaderEntry(Value, 'charset', LCharSet, QuoteMIME);{do not localize}
LTmp := RemoveHeaderEntry(LTmp, 'name', LName, QuoteMIME);{do not localize}
Headers.Values['Content-Type'] := LTmp;
{RLebeau: override the current values only if the header specifies new ones}
if LCharSet <> '' then begin
FCharSet := LCharSet;
end;
if LName <> '' then begin
FName := LName;
end;
end;
procedure TIdMessagePart.SetExtraHeaders(const Value: TIdHeaderList);
begin
FExtraHeaders.Assign(Value);
end;
{ TMessageParts }
function TIdMessageParts.Add: TIdMessagePart;
begin
// This helps prevent TIdMessagePart from being added
Result := nil;
end;
procedure TIdMessageParts.CountParts;
//TODO: Make AttCount, etc maintained on the fly
var
i: integer;
begin
FAttachmentCount := 0;
FRelatedPartCount := 0;
FTextPartCount := 0;
for i := 0 to Count - 1 do begin
if Length(TIdMessagePart(Items[i]).ContentID) > 0 then begin
Inc(FRelatedPartCount);
end;
case TIdMessagePart(Items[i]).PartType of
mptText :
begin
Inc(FTextPartCount)
end;
mptAttachment:
begin
Inc(FAttachmentCount);
end;
end;
end;
end;
constructor TIdMessageParts.Create(AOwner: TPersistent);
begin
inherited Create(AOwner, TIdMessagePart);
// Must set prop and not variable so it will initialize it
AttachmentEncoding := 'MIME'; {do not localize}
end;
function TIdMessageParts.GetItem(Index: Integer): TIdMessagePart;
begin
Result := TIdMessagePart(inherited GetItem(Index));
end;
function TIdMessageParts.GetOwnerMessage: TPersistent;
var
LOwner: TPersistent;
begin
LOwner := inherited GetOwner;
if LOwner is TIdMessage then begin
Result := LOwner;
end else begin
Result := nil;
end;
end;
procedure TIdMessageParts.SetAttachmentEncoding(const AValue: string);
begin
FMessageEncoderInfo := TIdMessageEncoderList.ByName(AValue);
FAttachmentEncoding := AValue;
end;
procedure TIdMessageParts.SetItem(Index: Integer; const Value: TIdMessagePart);
begin
inherited SetItem(Index, Value);
end;
end.