restemplate/indy/Protocols/IdCookieManager.pas

338 lines
8.7 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.5 2004.10.27 9:17:46 AM czhower
For TIdStrings
Rev 1.4 7/28/04 11:43:32 PM RLebeau
Bug fix for CleanupCookieList()
Rev 1.3 2004.02.03 5:45:02 PM czhower
Name changes
Rev 1.2 1/22/2004 7:10:02 AM JPMugaas
Tried to fix AnsiSameText depreciation.
Rev 1.1 2004.01.21 1:04:54 PM czhower
InitComponenet
Rev 1.0 11/14/2002 02:16:26 PM JPMugaas
2001-Mar-31 Doychin Bondzhev
- Added new method AddCookie2 that is called when we have Set-Cookie2 as response
- The common code in AddCookie and AddCookie2 is now in DoAdd
2001-Mar-24 Doychin Bondzhev
- Added OnNewCookie event
This event is called for every new cookie. Can be used to ask the user program
do we have to store this cookie in the cookie collection
- Added new method AddCookie
This calls the OnNewCookie event and if the result is true it adds the new cookie
in the collection
}
unit IdCookieManager;
{
Implementation of the HTTP State Management Mechanism as specified in RFC 6265.
Author: Remy Lebeau (remy@lebeausoftware.org)
Copyright: (c) Chad Z. Hower and The Indy Team.
}
interface
{$i IdCompilerDefines.inc}
uses
Classes,
IdBaseComponent,
IdCookie,
IdHeaderList,
IdURI;
Type
TOnNewCookieEvent = procedure(ASender: TObject; ACookie: TIdCookie; var VAccept: Boolean) of object;
TOnCookieManagerEvent = procedure(ASender: TObject; ACookieCollection: TIdCookies) of object;
TOnCookieCreateEvent = TOnCookieManagerEvent;
TOnCookieDestroyEvent = TOnCookieManagerEvent;
TIdCookieManager = class(TIdBaseComponent)
protected
FOnCreate: TOnCookieCreateEvent;
FOnDestroy: TOnCookieDestroyEvent;
FOnNewCookie: TOnNewCookieEvent;
FCookieCollection: TIdCookies;
procedure CleanupCookieList;
procedure DoOnCreate; virtual;
procedure DoOnDestroy; virtual;
function DoOnNewCookie(ACookie: TIdCookie): Boolean; virtual;
procedure InitComponent; override;
public
destructor Destroy; override;
//
procedure AddServerCookie(const ACookie: String; AURL: TIdURI);
procedure AddServerCookies(const ACookies: TStrings; AURL: TIdURI);
procedure AddCookies(ASource: TIdCookieManager);
procedure CopyCookie(ACookie: TIdCookie);
//
procedure GenerateClientCookies(AURL: TIdURI; SecureOnly: Boolean;
Headers: TIdHeaderList);
//
property CookieCollection: TIdCookies read FCookieCollection;
published
property OnCreate: TOnCookieCreateEvent read FOnCreate write FOnCreate;
property OnDestroy: TOnCookieDestroyEvent read FOnDestroy write FOnDestroy;
property OnNewCookie: TOnNewCookieEvent read FOnNewCookie write FOnNewCookie;
end;
//procedure SplitCookies(const ACookie: String; ACookies: TStrings);
implementation
uses
{$IFDEF HAS_UNIT_Generics_Defaults}
System.Generics.Defaults,
{$ENDIF}
IdAssignedNumbers, IdException, IdGlobal, IdGlobalProtocols, SysUtils;
{ TIdCookieManager }
destructor TIdCookieManager.Destroy;
begin
CleanupCookieList;
DoOnDestroy;
FreeAndNil(FCookieCollection);
inherited Destroy;
end;
function SortCookiesFunc({$IFDEF HAS_GENERICS_TList}const {$ENDIF}Item1, Item2: TIdCookie): Integer;
begin
// using the algorithm defined in RFC 6265 section 5.4
if Item1 = Item2 then
begin
Result := 0;
end
else if Length(Item2.Path) > Length(Item1.Path) then
begin
Result := 1;
end
else if Length(Item1.Path) = Length(Item2.Path) then
begin
if Item2.CreatedAt < Item1.CreatedAt then begin
Result := 1;
end else begin
Result := -1;
end;
end else
begin
Result := -1;
end;
end;
procedure TIdCookieManager.GenerateClientCookies(AURL: TIdURI; SecureOnly: Boolean;
Headers: TIdHeaderList);
var
I: Integer;
LCookieList: TIdCookieList;
LResultList: TIdCookieList;
LCookie: TIdCookie;
LCookiesToSend: String;
LNow: TDateTime;
begin
// check for expired cookies first...
CleanupCookieList;
LCookieList := CookieCollection.LockCookieList(caRead);
try
if LCookieList.Count > 0 then begin
LResultList := TIdCookieList.Create;
try
// Search for cookies for this domain and URI
for I := 0 to LCookieList.Count-1 do begin
LCookie := LCookieList[I];
if LCookie.IsAllowed(AURL, SecureOnly) then begin
LResultList.Add(LCookie);
end;
end;
if LResultList.Count > 0 then begin
if LResultList.Count > 1 then begin
LResultList.Sort(
{$IFDEF HAS_GENERICS_TList}
TComparer<TIdCookie>.Construct(SortCookiesFunc)
{$ELSE}
TListSortCompare(@SortCookiesFunc)
{$ENDIF}
);
end;
LNow := Now;
for I := 0 to LResultList.Count-1 do begin
LResultList[I].LastAccessed := LNow;
end;
LCookiesToSend := LResultList[0].ClientCookie;
for I := 1 to LResultList.Count-1 do begin
LCookiesToSend := LCookiesToSend + '; ' + LResultList[I].ClientCookie; {Do not Localize}
end;
Headers.AddValue('Cookie', LCookiesToSend); {Do not Localize}
end;
finally
LResultList.Free;
end;
end;
finally
CookieCollection.UnlockCookieList(caRead);
end;
end;
procedure TIdCookieManager.AddServerCookie(const ACookie: String; AURL: TIdURI);
var
LCookie: TIdCookie;
begin
// TODO: use TIdCookies.AddServerCookie() after adding
// a way for it to query the manager for rejections...
//
//FCookieCollection.AddServerCookie(ACookie, AURI);
LCookie := FCookieCollection.Add;
try
if LCookie.ParseServerCookie(ACookie, AURL) then
begin
if DoOnNewCookie(LCookie) then
begin
if FCookieCollection.AddCookie(LCookie, AURL) then begin
LCookie := nil;
Exit;
end;
end;
end;
finally
if LCookie <> nil then
begin
LCookie.Collection := nil;
LCookie.Free;
end;
end;
end;
procedure TIdCookieManager.AddCookies(ASource: TIdCookieManager);
begin
if (ASource <> nil) and (ASource <> Self) then begin
FCookieCollection.AddCookies(ASource.CookieCollection);
end;
end;
procedure TIdCookieManager.AddServerCookies(const ACookies: TStrings; AURL: TIdURI);
var
I: Integer;
begin
for I := 0 to ACookies.Count-1 do begin
AddServerCookie(ACookies[I], AURL);
end;
end;
procedure TIdCookieManager.CopyCookie(ACookie: TIdCookie);
var
LCookie: TIdCookie;
begin
LCookie := TIdCookieClass(ACookie.ClassType).Create(FCookieCollection);
try
LCookie.Assign(ACookie);
if LCookie.Domain <> '' then
begin
if DoOnNewCookie(LCookie) then
begin
if FCookieCollection.AddCookie(LCookie, nil) then begin
LCookie := nil;
end;
end;
end;
finally
if LCookie <> nil then
begin
LCookie.Collection := nil;
LCookie.Free;
end;
end;
end;
function TIdCookieManager.DoOnNewCookie(ACookie: TIdCookie): Boolean;
begin
Result := True;
if Assigned(FOnNewCookie) then begin
OnNewCookie(Self, ACookie, Result);
end;
end;
procedure TIdCookieManager.DoOnCreate;
begin
if Assigned(FOnCreate) then begin
OnCreate(Self, FCookieCollection);
end;
end;
procedure TIdCookieManager.DoOnDestroy;
begin
if Assigned(FOnDestroy) then
begin
OnDestroy(Self, FCookieCollection);
end;
end;
procedure TIdCookieManager.CleanupCookieList;
var
i: Integer;
LCookieList: TIdCookieList;
LCookie: TIdCookie;
begin
LCookieList := FCookieCollection.LockCookieList(caReadWrite);
try
for i := LCookieList.Count-1 downto 0 do
begin
LCookie := LCookieList[i];
if LCookie.IsExpired then
begin
// The Cookie has expired. It has to be removed from the collection
LCookieList.Delete(i);
// must set the Collection to nil or the cookie will try to remove
// itself from the cookie collection and deadlock
LCookie.Collection := nil;
LCookie.Free;
end;
end;
finally
FCookieCollection.UnlockCookieList(caReadWrite);
end;
end;
procedure TIdCookieManager.InitComponent;
begin
inherited InitComponent;
FCookieCollection := TIdCookies.Create(Self);
DoOnCreate;
end;
end.