Repository URL to install this package:
Version:
3.0.0 ▾
|
{
$Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
This file is part of the Free Component Library (FCL)
Copyright (c) 1999-2000 by the Free Pascal development team
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit iniwebsession;
{$mode objfpc}{$H+}
{ $define cgidebug}
interface
uses
Classes, SysUtils, fphttp, inifiles, httpdefs;
Type
{ TIniWebSession }
TIniWebSession = Class(TCustomSession)
Private
FSessionStarted : Boolean;
FCached: Boolean;
FIniFile : TMemInifile;
FSessionDir: String;
FTerminated :Boolean;
SID : String;
procedure UpdateIniFile;
Protected
Function CreateIniFile(Const AFN : String) : TMemIniFile; virtual;
Procedure FreeIniFile;
Procedure CheckSession;
Function GetSessionID : String; override;
Function GetSessionVariable(VarName : String) : String; override;
procedure SetSessionVariable(VarName : String; const AValue: String); override;
Property Cached : Boolean Read FCached Write FCached;
Property SessionDir : String Read FSessionDir Write FSessionDir;
Property IniFile : TMemIniFile Read FIniFile Write FIniFile;
Public
Destructor Destroy; override;
Procedure Terminate; override;
Procedure UpdateResponse(AResponse : TResponse); override;
Procedure InitSession(ARequest : TRequest; OnNewSession, OnExpired: TNotifyEvent); override;
Procedure InitResponse(AResponse : TResponse); override;
Procedure RemoveVariable(VariableName : String); override;
Function GetSessionDir : String;
end;
TIniWebSessionClass = Class of TIniWebSession;
{ TIniSessionFactory }
TIniSessionFactory = Class(TSessionFactory)
private
FCached: Boolean;
FOldFileNameScheme: Boolean;
FSessionDir: String;
procedure SetCached(const AValue: Boolean);
procedure SetSessionDir(const AValue: String);
protected
Procedure DeleteSessionFile(const AFileName : String);virtual;
Function SessionExpired(Ini : TMemIniFile) : boolean;
procedure CheckSessionDir; virtual;
Function DoCreateSession(ARequest : TRequest) : TCustomSession; override;
// Sweep session direcory and delete expired files.
procedure DoCleanupSessions; override;
Procedure DoDoneSession(Var ASession : TCustomSession); override;
Function SessionFilePrefix : String; virtual;
Public
// Directory where sessions are kept.
Property SessionDir : String Read FSessionDir Write SetSessionDir;
// Are ini files cached (written in 1 go before destroying)
Property Cached : Boolean Read FCached Write SetCached;
// If True, the '{' and '}' will not be stripped from the session filename.
Property OldFileNameScheme : Boolean Read FOldFileNameScheme Write FOldFileNameScheme;
end;
Var
IniWebSessionClass : TIniWebSessionClass = Nil;
Const
MaxIniCreate = 5;
implementation
{$ifdef cgidebug}
uses dbugintf;
{$endif}
Const
// Sections in ini file
SSession = 'Session';
SData = 'Data';
KeyStart = 'Start'; // Start time of session
KeyLast = 'Last'; // Last seen time of session
KeyTimeOut = 'Timeout'; // Timeout in seconds;
resourcestring
SErrSessionTerminated = 'No web session active: Session was terminated';
SErrNoSession = 'No web session active: Session was not started';
{ TIniSessionFactory }
procedure TIniSessionFactory.SetCached(const AValue: Boolean);
begin
if FCached=AValue then exit;
FCached:=AValue;
end;
procedure TIniSessionFactory.SetSessionDir(const AValue: String);
begin
if FSessionDir=AValue then exit;
FSessionDir:=AValue;
end;
procedure TIniSessionFactory.DeleteSessionFile(const AFileName: String);
begin
DeleteFile(AFileName); // TODO : silently ignoring errors ?
end;
function TIniSessionFactory.SessionExpired(Ini: TMemIniFile): boolean;
Var
L : TDateTime;
T : Integer;
begin
L:=Ini.ReadDateTime(SSession,KeyLast,0);
T:=Ini.ReadInteger(SSession,KeyTimeOut,DefaultTimeOutMinutes);
{$ifdef cgidebug}
If (L=0) then
SendDebug('No datetime in inifile (or not valid datetime : '+Ini.ReadString(SSession,KeyLast,''))
else
SendDebug('Last :'+FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz',L));
SendDebug('Timeout :'+IntToStr(t));
{$endif}
Result:=((Now-L)>(T/(24*60)));
{$ifdef cgidebug}
if Result then
begin
SendDebug('Timeout :'+FloatToStr(T/(24*60)));
SendDebug('Timeout :'+FormatDateTime('hh:nn:ss.zzz',(T/(24*60))));
SendDebug('Diff :'+FormatDateTime('hh:nn:ss.zzz',Now-L));
SendDebug('Ini file session expired: '+ExtractFileName(Ini.FileName));
end;
{$endif}
end;
procedure TIniSessionFactory.CheckSessionDir;
Var
TD : String;
begin
If (FSessionDir='') then
begin
TD:=IncludeTrailingPathDelimiter(GetTempDir(True));
FSessionDir:=TD+'fpwebsessions'+PathDelim;
if Not ForceDirectories(FSessionDir) then
FSessionDir:=TD; // Assuming temp dir is writeable as fallback
end;
end;
function TIniSessionFactory.DoCreateSession(ARequest: TRequest): TCustomSession;
Var
S : TIniWebSession;
begin
CheckSessionDir;
if IniWebSessionClass=Nil then
S:=TIniWebSession.Create(Nil)
else
S:=IniWebSessionClass.Create(Nil);
S.SessionDir:=SessionDir;
S.SessionCookie:=SessionCookie;
S.SessionCookiePath:=SessionCookiePath;
S.Cached:=Cached;
Result:=S;
end;
procedure TIniSessionFactory.DoCleanupSessions;
Var
Info : TSearchRec;
Ini : TMemIniFile;
FN : string;
begin
CheckSessionDir;
If FindFirst(SessionDir+AllFilesMask,0,info)=0 then
try
Repeat
if (Info.Attr and faDirectory=0) then
begin
FN:=SessionDir+Info.Name;
Ini:=TMeminiFile.Create(FN);
try
If SessionExpired(Ini) then
DeleteSessionFile(FN);
finally
Ini.Free;
end;
end;
Until FindNext(Info)<>0;
finally
FindClose(Info);
end;
end;
procedure TIniSessionFactory.DoDoneSession(var ASession: TCustomSession);
begin
FreeAndNil(ASession);
end;
Function TIniSessionFactory.SessionFilePrefix : String;
begin
Result:='';
end;
{ TIniWebSession }
Function TIniWebSession.GetSessionDir : String;
begin
Result:=SessionDir;
end;
function TIniWebSession.GetSessionID: String;
begin
If (SID='') then
SID:=inherited GetSessionID;
Result:=SID;
end;
Procedure TIniWebSession.UpdateIniFile;
Var
ACount : Integer;
OK : Boolean;
begin
ACount:=0;
OK:=False;
repeat
Inc(ACount);
try
TMemIniFile(FIniFile).UpdateFile;
OK:=True;
except
On E : EFCreateError do
begin
If ACount>MaxIniCreate then
Raise;
Sleep(20);
end;
On E : EFOpenError do
begin
If ACount>MaxIniCreate then
Raise;
Sleep(20);
end;
On E : Exception do
Raise;
end;
Until OK;
end;
function TIniWebSession.CreateIniFile(Const AFN: String): TMemIniFile;
Var
ACount : Integer;
begin
ACount:=0;
Result:=Nil;
repeat
Inc(ACount);
try
Result:=TMemIniFile.Create(AFN,False);
except
On E : EFCreateError do
begin
If ACount>MaxIniCreate then
Raise;
Sleep(20);
end;
On E : EFOpenError do
begin
If ACount>MaxIniCreate then
Raise;
Sleep(20);
end;
On E : Exception do
Raise;
end;
until (Result<>Nil);
end;
procedure TIniWebSession.FreeIniFile;
begin
If Cached and Assigned(FIniFile) then
UpdateIniFile;
FreeAndNil(FIniFile);
end;
Procedure TIniWebSession.CheckSession;
begin
If Not Assigned(FInifile) then
if FTerminated then
Raise EWebSessionError.Create(SErrSessionTerminated)
else
Raise EWebSessionError.Create(SErrNoSession)
end;
function TIniWebSession.GetSessionVariable(VarName: String): String;
begin
CheckSession;
Result:=FIniFile.ReadString(SData,VarName,'');
end;
procedure TIniWebSession.SetSessionVariable(VarName: String;
const AValue: String);
begin
CheckSession;
FIniFile.WriteString(SData,VarName,AValue);
If Not Cached then
UpdateIniFile;
end;
destructor TIniWebSession.Destroy;
begin
// In case an exception occured and UpdateResponse is not called,
// write the updates to disk and free FIniFile
FreeIniFile;
inherited Destroy;
end;
procedure TIniWebSession.Terminate;
begin
FTerminated:=True;
If Assigned(FIniFile) Then
begin
DeleteFile(Finifile.FileName);
FreeAndNil(FIniFile);
end;
end;
procedure TIniWebSession.UpdateResponse(AResponse: TResponse);
begin
// Do nothing. Init has done the job.
FreeIniFile;
end;
procedure TIniWebSession.InitSession(ARequest: TRequest; OnNewSession,OnExpired: TNotifyEvent);
Var
S,FN : String;
SF : TIniSessionFactory;
begin
SF:=SessionFactory as TIniSessionFactory;
{$ifdef cgidebug}SendMethodEnter('TIniWebSession.InitSession');{$endif}
// First initialize all session-dependent properties to their default, because
// in Apache-modules or fcgi programs the session-instance is re-used
SID := '';
FSessionStarted := False;
FTerminated := False;
// If a exception occured during a prior request FIniFile is still not freed
if assigned(FIniFile) then FreeIniFile;
If (SessionCookie='') then
SessionCookie:=SFPWebSession;
S:=ARequest.CookieFields.Values[SessionCookie];
// have session cookie ?
If (S<>'') then
begin
FN:=IncludeTrailingPathDelimiter(SessionDir)+SF.SessionFilePrefix+S;
{$ifdef cgidebug}SendDebug('Existing session. Reading ini file:'+FN);{$endif}
FIniFile:=CreateIniFile(FN);
if SF.SessionExpired(FIniFile) then
begin
// Expire session.
If Assigned(OnExpired) then
OnExpired(Self);
SF.DeleteSessionFile(FIniFIle.FileName);
FreeAndNil(FInifile);
S:='';
end
else
SID:=S;
end;
If (S='') then
begin
If Assigned(OnNewSession) then
OnNewSession(Self);
GetSessionID;
S:=IncludeTrailingPathDelimiter(SessionDir)+SF.SessionFilePrefix+SessionID;
{$ifdef cgidebug}SendDebug('Expired or new session. Creating new Ini file : '+S);{$endif}
FIniFile:=CreateIniFile(S);
FIniFile.WriteDateTime(SSession,KeyStart,Now);
FIniFile.WriteInteger(SSession,KeyTimeOut,Self.TimeOutMinutes);
FSessionStarted:=True;
end;
FIniFile.WriteDateTime(SSession,KeyLast,Now);
If not FCached then
UpdateIniFile;
{$ifdef cgidebug}SendMethodExit('TIniWebSession.InitSession');{$endif}
end;
procedure TIniWebSession.InitResponse(AResponse: TResponse);
Var
C : TCookie;
begin
{$ifdef cgidebug}SendMethodEnter('TIniWebSession.InitResponse: '+SID);{$endif}
C:=AResponse.Cookies.FindCookie(SessionCookie);
If (C=Nil) then
begin
C:=AResponse.Cookies.Add;
C.Name:=SessionCookie;
end;
If FTerminated then
begin
{$ifdef cgidebug}SendDebug('Session terminated');{$endif}
C.Value:='';
end
else
begin
{$ifdef cgidebug}SendDebug('Existing session or Session started');{$endif}
C.Value:=SID;
C.Path:=SessionCookiePath;
end;
{$ifdef cgidebug}SendMethodExit('TIniWebSession.InitResponse');{$endif}
end;
procedure TIniWebSession.RemoveVariable(VariableName: String);
begin
{$ifdef cgidebug}SendMethodEnter('TIniWebSession.RemoveVariable');{$endif}
CheckSession;
FIniFile.DeleteKey(SData,VariableName);
If Not Cached then
UpdateIniFile;
{$ifdef cgidebug}SendMethodExit('TIniWebSession.RemoveVariable');{$endif}
end;
initialization
SessionFactoryClass:=TIniSessionFactory;
end.