| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- {
- $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
- 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);
- IndyAddPair(ADest, LName, LValue);
- 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.
|