restemplate/indy/Protocols/IdHeaderList.pas

508 lines
14 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.9 10/26/2004 10:10:58 PM JPMugaas
Updated refs.
Rev 1.8 3/6/2004 2:53:30 PM JPMugaas
Cleaned up an if as per Bug #79.
Rev 1.7 2004.02.03 5:43:42 PM czhower
Name changes
Rev 1.6 2004.01.27 1:39:26 AM czhower
CharIsInSet bug fix
Rev 1.5 1/22/2004 3:50:04 PM SPerry
fixed set problems (with CharIsInSet)
Rev 1.4 1/22/2004 7:10:06 AM JPMugaas
Tried to fix AnsiSameText depreciation.
Rev 1.3 10/5/2003 11:43:50 PM GGrieve
Use IsLeadChar
Rev 1.2 10/4/2003 9:15:14 PM GGrieve
DotNet changes
Rev 1.1 2/25/2003 12:56:20 PM JPMugaas
Updated with Hadi's fix for a bug . If complete boolean expression i on, you
may get an Index out of range error.
Rev 1.0 11/13/2002 07:53:52 AM JPMugaas
2002-Jan-27 Don Siders
- Modified FoldLine to include Comma in break character set.
2000-May-31 J. Peter Mugaas
- started this class to facilitate some work on Indy so we don't have to
convert '=' to ":" and vice-versa just to use the Values property.
}
unit IdHeaderList;
{
NOTE: This is a modification of Borland's TStrings definition in a
TStringList descendant. I had to conceal the original Values to do
this since most of low level property setting routines aren't virtual
and are private.
}
interface
{$i IdCompilerDefines.inc}
uses
Classes, IdGlobalProtocols;
type
TIdHeaderList = class(TStringList)
protected
FNameValueSeparator : String;
FUnfoldLines : Boolean;
FFoldLines : Boolean;
FFoldLinesLength : Integer;
FQuoteType: TIdHeaderQuotingType;
//
procedure AssignTo(Dest: TPersistent); override;
{This deletes lines which were folded}
Procedure DeleteFoldedLines(Index : Integer);
{This folds one line into several lines}
function FoldLine(AString : string): TStrings; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use FoldLineToList()'{$ENDIF};{$ENDIF}
procedure FoldLineToList(AString : string; ALines: TStrings);
{Folds lines and inserts them into a position, Index}
procedure FoldAndInsert(AString : String; Index : Integer);
{Name property get method}
function GetName(Index: Integer): string;
{Value property get method}
function GetValue(const AName: string): string;
{Value property get method}
function GetParam(const AName, AParam: string): string;
function GetAllParams(const AName: string): string;
{Value property set method}
procedure SetValue(const AName, AValue: string);
{Value property set method}
procedure SetParam(const AName, AParam, AValue: string);
procedure SetAllParams(const AName, AValue: string);
{Gets a value from a string}
function GetValueFromLine(var VLine : Integer) : String;
procedure SkipValueAtLine(var VLine : Integer);
public
procedure AddStrings(Strings: TStrings); override;
{ This method extracts "name=value" strings from the ASrc TStrings and adds
them to this list using our delimiter defined in NameValueSeparator. }
procedure AddStdValues(ASrc: TStrings);
{ This method adds a single name/value pair to this list using our delimiter
defined in NameValueSeparator. }
procedure AddValue(const AName, AValue: string); // allows duplicates
{ This method extracts all of the values from this list and puts them in the
ADest TStrings as "name=value" strings.}
procedure ConvertToStdValues(ADest: TStrings);
constructor Create(AQuoteType: TIdHeaderQuotingType);
{ This method, given a name specified by AName, extracts all of the values
for that name and puts them in a new string list (just the values) one
per line in the ADest TIdStrings.}
procedure Extract(const AName: string; ADest: TStrings);
{ This property works almost exactly as Borland's IndexOfName except it
uses our delimiter defined in NameValueSeparator }
function IndexOfName(const AName: string): Integer; reintroduce;
{ This property works almost exactly as Borland's Names except it uses
our delimiter defined in NameValueSeparator }
property Names[Index: Integer]: string read GetName;
{ This property works almost exactly as Borland's Values except it uses
our delimiter defined in NameValueSeparator }
property Values[const Name: string]: string read GetValue write SetValue;
property Params[const Name, Param: string]: string read GetParam write SetParam;
property AllParams[const Name: string]: string read GetAllParams write SetAllParams;
{ This is the separator we need to separate the name from the value }
property NameValueSeparator : String read FNameValueSeparator
write FNameValueSeparator;
{ Should we unfold lines so that continuation header data is returned as
well}
property UnfoldLines : Boolean read FUnfoldLines write FUnfoldLines;
{ Should we fold lines we the Values(x) property is set with an
assignment }
property FoldLines : Boolean read FFoldLines write FFoldLines;
{ The Wrap position for our folded lines }
property FoldLength : Integer read FFoldLinesLength write FFoldLinesLength;
end;
implementation
uses
IdException,
IdGlobal,
SysUtils;
{ TIdHeaderList }
procedure TIdHeaderList.AddStdValues(ASrc: TStrings);
var
i: integer;
begin
BeginUpdate;
try
for i := 0 to ASrc.Count - 1 do begin
AddValue(ASrc.Names[i], IndyValueFromIndex(ASrc, i));
end;
finally
EndUpdate;
end;
end;
procedure TIdHeaderList.AddValue(const AName, AValue: string);
var
I: Integer;
begin
if (AName <> '') and (AValue <> '') then begin {Do not Localize}
I := Add(''); {Do not Localize}
if FFoldLines then begin
FoldAndInsert(AName + FNameValueSeparator + AValue, I);
end else begin
Put(I, AName + FNameValueSeparator + AValue);
end;
end;
end;
procedure TIdHeaderList.AddStrings(Strings: TStrings);
begin
if Strings is TIdHeaderList then begin
inherited AddStrings(Strings);
end else begin
AddStdValues(Strings);
end;
end;
procedure TIdHeaderList.AssignTo(Dest: TPersistent);
begin
if (Dest is TStrings) and not (Dest is TIdHeaderList) then begin
ConvertToStdValues(TStrings(Dest));
end else begin
inherited AssignTo(Dest);
end;
end;
procedure TIdHeaderList.ConvertToStdValues(ADest: TStrings);
var
idx: Integer;
LName, LValue: string;
begin
ADest.BeginUpdate;
try
idx := 0;
while idx < Count do
begin
LName := GetName(idx);
LValue := GetValueFromLine(idx);
// TODO: use ADest.NameValueSeparator on platforms that support it
ADest.Add(LName + '=' + LValue); {do not localize}
end;
finally
ADest.EndUpdate;
end;
end;
constructor TIdHeaderList.Create(AQuoteType: TIdHeaderQuotingType);
begin
inherited Create;
FNameValueSeparator := ': '; {Do not Localize}
FUnfoldLines := True;
FFoldLines := True;
{ 78 was specified by a message draft available at
http://www.imc.org/draft-ietf-drums-msg-fmt }
// HTTP does not technically have a limitation on line lengths
FFoldLinesLength := iif(AQuoteType = QuoteHTTP, MaxInt, 78);
FQuoteType := AQuoteType;
end;
procedure TIdHeaderList.DeleteFoldedLines(Index: Integer);
begin
Inc(Index); {skip the current line}
if Index < Count then begin
while (Index < Count) and CharIsInSet(Get(Index), 1, LWS) do begin {Do not Localize}
Delete(Index);
end;
end;
end;
procedure TIdHeaderList.Extract(const AName: string; ADest: TStrings);
var
idx : Integer;
begin
if Assigned(ADest) then begin
ADest.BeginUpdate;
try
idx := 0;
while idx < Count do
begin
if TextIsSame(AName, GetName(idx)) then begin
ADest.Add(GetValueFromLine(idx));
end else begin
SkipValueAtLine(idx);
end;
end;
finally
ADest.EndUpdate;
end;
end;
end;
procedure TIdHeaderList.FoldAndInsert(AString : String; Index: Integer);
var
LStrs : TStrings;
idx : Integer;
begin
LStrs := TStringList.Create;
try
FoldLineToList(AString, LStrs);
idx := LStrs.Count - 1;
Put(Index, LStrs[idx]);
{We decrement by one because we put the last string into the HeaderList}
Dec(idx);
while idx > -1 do
begin
Insert(Index, LStrs[idx]);
Dec(idx);
end;
finally
FreeAndNil(LStrs);
end; //finally
end;
{$I IdDeprecatedImplBugOff.inc}
function TIdHeaderList.FoldLine(AString : string): TStrings;
{$I IdDeprecatedImplBugOn.inc}
begin
Result := TStringList.Create;
try
FoldLineToList(AString, Result);
except
FreeAndNil(Result);
raise;
end;
end;
procedure TIdHeaderList.FoldLineToList(AString : string; ALines: TStrings);
var
s : String;
begin
{we specify a space so that starts a folded line}
s := IndyWrapText(AString, EOL+' ', LWS+',', FFoldLinesLength); {Do not Localize}
if s <> '' then begin
ALines.BeginUpdate;
try
repeat
ALines.Add(TrimRight(Fetch(s, EOL)));
until s = ''; {Do not Localize};
finally
ALines.EndUpdate;
end;
end;
end;
function TIdHeaderList.GetName(Index: Integer): string;
var
I : Integer;
begin
Result := Get(Index);
{We trim right to remove space to accomodate header errors such as
Message-ID:<asdf@fdfs
}
I := IndyPos(TrimRight(FNameValueSeparator), Result);
if I <> 0 then begin
SetLength(Result, I - 1);
end else begin
SetLength(Result, 0);
end;
end;
function TIdHeaderList.GetValue(const AName: string): string;
var
idx: Integer;
begin
idx := IndexOfName(AName);
Result := GetValueFromLine(idx);
end;
function TIdHeaderList.GetValueFromLine(var VLine: Integer): String;
var
LLine, LSep: string;
P: Integer;
begin
if (VLine >= 0) and (VLine < Count) then begin
LLine := Get(VLine);
Inc(VLine);
{We trim right to remove space to accomodate header errors such as
Message-ID:<asdf@fdfs
}
LSep := TrimRight(FNameValueSeparator);
P := IndyPos(LSep, LLine);
Result := TrimLeft(Copy(LLine, P + Length(LSep), MaxInt));
if FUnfoldLines then begin
while VLine < Count do begin
LLine := Get(VLine);
// s[1] is safe since header lines cannot be empty as that causes then end of the header block
if not CharIsInSet(LLine, 1, LWS) then begin
Break;
end;
Result := Trim(Result) + ' ' + Trim(LLine); {Do not Localize}
Inc(VLine);
end;
end;
// User may be fetching a folded line directly.
Result := Trim(Result);
end else begin
Result := ''; {Do not Localize}
end;
end;
procedure TIdHeaderList.SkipValueAtLine(var VLine: Integer);
begin
if (VLine >= 0) and (VLine < Count) then begin
Inc(VLine);
if FUnfoldLines then begin
while VLine < Count do begin
// s[1] is safe since header lines cannot be empty as that causes then end of the header block
if not CharIsInSet(Get(VLine), 1, LWS) then begin
Break;
end;
Inc(VLine);
end;
end;
end;
end;
function TIdHeaderList.GetParam(const AName, AParam: string): string;
var
s: string;
LQuoteType: TIdHeaderQuotingType;
begin
s := Values[AName];
if s <> '' then begin
LQuoteType := FQuoteType;
case LQuoteType of
QuoteRFC822: begin
if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) <> -1 then begin {Do not Localize}
LQuoteType := QuoteMIME;
end;
end;
QuoteMIME: begin
if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) = -1 then begin {Do not Localize}
LQuoteType := QuoteRFC822;
end;
end;
end;
Result := ExtractHeaderSubItem(s, AParam, LQuoteType);
end else begin
Result := '';
end;
end;
function TIdHeaderList.GetAllParams(const AName: string): string;
var
s: string;
begin
s := Values[AName];
if s <> '' then begin
Fetch(s, ';'); {do not localize}
Result := Trim(s);
end else begin
Result := '';
end;
end;
function TIdHeaderList.IndexOfName(const AName: string): Integer;
var
i: Integer;
begin
Result := -1;
for i := 0 to Count - 1 do begin
if TextIsSame(GetName(i), AName) then begin
Result := i;
Exit;
end;
end;
end;
procedure TIdHeaderList.SetValue(const AName, AValue: string);
var
I: Integer;
begin
I := IndexOfName(AName);
if AValue <> '' then begin {Do not Localize}
if I < 0 then begin
I := Add(''); {Do not Localize}
end;
if FFoldLines then begin
DeleteFoldedLines(I);
FoldAndInsert(AName + FNameValueSeparator + AValue, I);
end else begin
Put(I, AName + FNameValueSeparator + AValue);
end;
end
else if I >= 0 then begin
if FFoldLines then begin
DeleteFoldedLines(I);
end;
Delete(I);
end;
end;
procedure TIdHeaderList.SetParam(const AName, AParam, AValue: string);
var
LQuoteType: TIdHeaderQuotingType;
begin
LQuoteType := FQuoteType;
case LQuoteType of
QuoteRFC822: begin
if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) <> -1 then begin {Do not Localize}
LQuoteType := QuoteMIME;
end;
end;
QuoteMIME: begin
if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) = -1 then begin {Do not Localize}
LQuoteType := QuoteRFC822;
end;
end;
end;
Values[AName] := ReplaceHeaderSubItem(Values[AName], AParam, AValue, LQuoteType);
end;
procedure TIdHeaderList.SetAllParams(const AName, AValue: string);
var
LValue: string;
begin
LValue := Values[AName];
if LValue <> '' then
begin
LValue := ExtractHeaderItem(LValue);
if AValue <> '' then begin
LValue := LValue + '; ' + AValue; {do not localize}
end;
Values[AName] := LValue;
end;
end;
end.