| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- { $HDR$}
- {**********************************************************************}
- { Unit archived using Team Coherence }
- { Team Coherence is Copyright 2002 by Quality Software Components }
- { }
- { For further information / comments, visit our WEB site at }
- { http://www.TeamCoherence.com }
- {**********************************************************************}
- {}
- { $Log: 10185: IdHeaderList.pas
- {
- { Rev 1.1 2/25/2003 12:47:34 PM JPMugaas
- { Updated with Hadi's fix. If complete boolean evaluation was on, the code
- { could sometimes with an index out of range.
- }
- {
- { Rev 1.0 2002.11.12 10:40:38 PM czhower
- }
- unit IdHeaderList;
- {
- 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.
- }
- {
- 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
- uses
- Classes;
- type
- TIdHeaderList = class(TStringList)
- protected
- FNameValueSeparator : String;
- FCaseSensitive : Boolean;
- FUnfoldLines : Boolean;
- FFoldLines : Boolean;
- FFoldLinesLength : Integer;
- //
- {This deletes lines which were folded}
- Procedure DeleteFoldedLines(Index : Integer);
- {This folds one line into several lines}
- function FoldLine(AString : string) : TStringList;
- {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 set method}
- procedure SetValue(const Name, Value: string);
- {Gets a value from a string}
- function GetValueFromLine(ALine : Integer) : String;
- Function GetNameFromLine(ALine : Integer) : String;
- public
- procedure AddStdValues(ASrc: TStrings);
- procedure ConvertToStdValues(ADest: TStrings);
- constructor Create;
- { 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 TStrings.}
- procedure Extract(const AName: string; ADest: TStrings);
- { This property works almost exactly as Borland's IndexOfName except it uses
- our deliniator defined in NameValueSeparator }
- function IndexOfName(const AName: string): Integer; reintroduce;
- { This property works almost exactly as Borland's Values except it uses
- our deliniator defined in NameValueSeparator }
- property Names[Index: Integer]: string read GetName;
- { This property works almost exactly as Borland's Values except it uses
- our deliniator defined in NameValueSeparator }
- property Values[const Name: string]: string read GetValue write SetValue;
- { This is the separator we need to separate the name from the value }
- property NameValueSeparator : String read FNameValueSeparator
- write FNameValueSeparator;
- { Should the names be tested in a case-senstive manner. }
- property CaseSensitive : Boolean read FCaseSensitive write FCaseSensitive;
- { 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;
- {This is taken from Borland's SysUtils and modified for our folding} {Do not Localize}
- function FoldWrapText(const Line, BreakStr: string; BreakChars: TSysCharSet;
- MaxCol: Integer): string;
- const
- QuoteChars = ['"']; {Do not Localize}
- var
- Col, Pos: Integer;
- LinePos, LineLen: Integer;
- BreakLen, BreakPos: Integer;
- QuoteChar, CurChar: Char;
- ExistingBreak: Boolean;
- begin
- Col := 1;
- Pos := 1;
- LinePos := 1;
- BreakPos := 0;
- QuoteChar := ' '; {Do not Localize}
- ExistingBreak := False;
- LineLen := Length(Line);
- BreakLen := Length(BreakStr);
- Result := ''; {Do not Localize}
- while Pos <= LineLen do
- begin
- CurChar := Line[Pos];
- if CurChar in LeadBytes then
- begin
- Inc(Pos);
- Inc(Col);
- end //if CurChar in LeadBytes then
- else
- if CurChar = BreakStr[1] then
- begin
- if QuoteChar = ' ' then {Do not Localize}
- begin
- ExistingBreak := AnsiSameText(BreakStr, Copy(Line, Pos, BreakLen));
- if ExistingBreak then
- begin
- Inc(Pos, BreakLen-1);
- BreakPos := Pos;
- end; //if ExistingBreak then
- end // if QuoteChar = ' ' then {Do not Localize}
- end // if CurChar = BreakStr[1] then
- else
- if CurChar in BreakChars then
- begin
- if QuoteChar = ' ' then {Do not Localize}
- BreakPos := Pos
- end // if CurChar in BreakChars then
- else
- if CurChar in QuoteChars then
- if CurChar = QuoteChar then
- QuoteChar := ' ' {Do not Localize}
- else
- if QuoteChar = ' ' then {Do not Localize}
- QuoteChar := CurChar;
- Inc(Pos);
- Inc(Col);
- if not (QuoteChar in QuoteChars) and (ExistingBreak or
- ((Col > MaxCol) and (BreakPos > LinePos))) then
- begin
- Col := Pos - BreakPos;
- Result := Result + Copy(Line, LinePos, BreakPos - LinePos + 1);
- if not (CurChar in QuoteChars) then
- while (Pos <= LineLen) and (Line[Pos] in BreakChars + [#13, #10]) do Inc(Pos);
- if not ExistingBreak and (Pos < LineLen) then
- Result := Result + BreakStr;
- Inc(BreakPos);
- LinePos := BreakPos;
- ExistingBreak := False;
- end; //if not
- end; //while Pos <= LineLen do
- Result := Result + Copy(Line, LinePos, MaxInt);
- end;
- { TIdHeaderList }
- procedure TIdHeaderList.AddStdValues(ASrc: TStrings);
- var
- i: integer;
- begin
- for i := 0 to ASrc.Count - 1 do begin
- Add(StringReplace(ASrc[i], '=', NameValueSeparator, [])); {Do not Localize}
- end;
- end;
- procedure TIdHeaderList.ConvertToStdValues(ADest: TStrings);
- var
- i: integer;
- begin
- for i := 0 to Count - 1 do begin
- ADest.Add(StringReplace(Strings[i], NameValueSeparator, '=', [])); {Do not Localize}
- end;
- end;
- constructor TIdHeaderList.Create;
- begin
- inherited Create;
- FNameValueSeparator := ': '; {Do not Localize}
- FCaseSensitive := False;
- FUnfoldLines := True;
- FFoldLines := True;
- { 78 was specified by a message draft available at
- http://www.imc.org/draft-ietf-drums-msg-fmt }
- FFoldLinesLength := 78;
- end;
- procedure TIdHeaderList.DeleteFoldedLines(Index: Integer);
- begin
- Inc(Index); {skip the current line}
- if Index < Count then begin
- while ( Index < Count ) and ( ( Length( Get( Index ) ) > 0) and
- ( Get( Index ) [ 1 ] = ' ' ) or ( Get( Index ) [ 1 ] = #9 ) ) do {Do not Localize}
- begin
- Delete( Index );
- end; //while
- end;
- end;
- procedure TIdHeaderList.Extract(const AName: string; ADest: TStrings);
- var idx : Integer;
- begin
- if not Assigned(ADest) then
- Exit;
- for idx := 0 to Count - 1 do
- begin
- if AnsiSameText(AName, GetNameFromLine(idx)) then
- begin
- ADest.Add(GetValueFromLine(idx));
- end;
- end;
- end;
- procedure TIdHeaderList.FoldAndInsert(AString : String; Index: Integer);
- var strs : TStringList;
- idx : Integer;
- begin
- strs := FoldLine( AString );
- try
- idx := strs.Count - 1;
- Put(Index, strs [ idx ] );
- {We decrement by one because we put the last string into the HeaderList}
- Dec( idx );
- while ( idx > -1 ) do
- begin
- Insert(Index, strs [ idx ] );
- Dec( idx );
- end;
- finally
- FreeAndNil( strs );
- end; //finally
- end;
- function TIdHeaderList.FoldLine(AString : string): TStringList;
- var s : String;
- begin
- Result := TStringList.Create;
- try
- {we specify a space so that starts a folded line}
- s := FoldWrapText(AString, EOL+' ', LWS+[','], FFoldLinesLength); {Do not Localize}
- while s <> '' do {Do not Localize}
- begin
- Result.Add( TrimRight( Fetch( s, EOL ) ) );
- end; // while s <> '' do {Do not Localize}
- finally
- end; //try..finally
- end;
- function TIdHeaderList.GetName(Index: Integer): string;
- var
- P: Integer;
- begin
- Result := Get( Index );
- P := IndyPos( FNameValueSeparator , Result );
- if P <> 0 then
- begin
- SetLength( Result, P - 1 );
- end // if P <> 0 then
- else
- begin
- SetLength( Result, 0 );
- end; // else if P <> 0 then
- Result := Result;
- end;
- function TIdHeaderList.GetNameFromLine(ALine: Integer): String;
- var p : Integer;
- begin
- Result := Get( ALine );
- if not FCaseSensitive then
- begin
- Result := UpperCase( Result );
- end; // if not FCaseSensitive then
- {We trim right to remove space to accomodate header errors such as
- Message-ID:<asdf@fdfs
- }
- P := IndyPos( TrimRight( FNameValueSeparator ), Result );
- Result := Copy( Result, 1, P - 1 );
- end;
- function TIdHeaderList.GetValue(const AName: string): string;
- begin
- Result := GetValueFromLine(IndexOfName(AName));
- end;
- function TIdHeaderList.GetValueFromLine(ALine: Integer): String;
- var
- LFoldedLine: string;
- LName: string;
- begin
- if (ALine >= 0) and (ALine < Count) then begin
- LName := GetNameFromLine(ALine);
- Result := Copy(Get(ALine), Length(LName) + 2, MaxInt);
- if FUnfoldLines then begin
- while True do begin
- Inc(ALine);
- if ALine = Count then begin
- Break;
- end;
- LFoldedLine := Get(ALine);
- // s[1] is safe since header lines cannot be empty as that causes then end of the header block
- if not (LFoldedLine[1] in LWS) then begin
- Break;
- end;
- Result := Trim(Result) + ' ' + Trim(LFoldedLine); {Do not Localize}
- end;
- end;
- end else begin
- Result := ''; {Do not Localize}
- end;
- // User may be fetching an folded line diretly.
- Result := Trim(Result);
- end;
- function TIdHeaderList.IndexOfName(const AName: string): Integer;
- var
- i: Integer;
- begin
- Result := -1;
- for i := 0 to Count - 1 do begin
- if AnsiSameText(GetNameFromLine(i), AName) then begin
- Result := i;
- Break;
- end;
- end;
- end;
- procedure TIdHeaderList.SetValue(const Name, Value: string);
- var
- I: Integer;
- begin
- I := IndexOfName(Name);
- if Value <> '' then {Do not Localize}
- begin
- if I < 0 then
- begin
- I := Add( '' ); {Do not Localize}
- end; //if I < 0 then
- if FFoldLines then
- begin
- DeleteFoldedLines( I );
- FoldAndInsert( Name + FNameValueSeparator + Value, I );
- end
- else
- begin
- Put( I, Name + FNameValueSeparator + Value );
- end; //else..FFoldLines
- end //if Value <> '' then {Do not Localize}
- else
- begin
- if I >= 0 then
- begin
- if FFoldLines then
- begin
- DeleteFoldedLines( I );
- end;
- Delete( I );
- end; //if I >= 0 then
- end; //else .. if Value <> '' then {Do not Localize}
- end;
- end.
|