websession.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit websession;
  12. {$mode objfpc}{$H+}
  13. { $define cgidebug}
  14. interface
  15. uses
  16. Classes, SysUtils, fphttp, inifiles, httpdefs;
  17. Type
  18. { TSessionHTTPModule }
  19. TSessionHTTPModule = Class(TCustomHTTPModule)
  20. Private
  21. FCreateSession : Boolean;
  22. FOnNewSession: TNotifyEvent;
  23. FOnSessionExpired: TNotifyEvent;
  24. FSession: TCustomSession;
  25. function GetSession: TCustomSession;
  26. procedure SetSession(const AValue: TCustomSession);
  27. Protected
  28. Procedure CheckSession(ARequest : TRequest);
  29. Procedure InitSession(AResponse : TResponse);
  30. Procedure UpdateSession(AResponse : TResponse);
  31. Procedure DoneSession; virtual;
  32. Public
  33. destructor destroy; override;
  34. Procedure Notification(AComponent : TComponent;Operation : TOperation); override;
  35. Procedure Loaded; Override;
  36. Property CreateSession : Boolean Read FCreateSession Write FCreateSession;
  37. Property Session : TCustomSession Read GetSession Write SetSession;
  38. Property OnNewSession : TNotifyEvent Read FOnNewSession Write FOnNewSession;
  39. Property OnSessionExpired : TNotifyEvent Read FOnSessionExpired Write FOnSessionExpired;
  40. end;
  41. { TIniWebSession }
  42. TIniWebSession = Class(TCustomSession)
  43. Private
  44. FSessionStarted : Boolean;
  45. FCached: Boolean;
  46. FIniFile : TMemInifile;
  47. FSessionCookie: String;
  48. FSessionCookiePath: String;
  49. FSessionDir: String;
  50. FTerminated :Boolean;
  51. SID : String;
  52. private
  53. procedure FreeIniFile;
  54. function GetSessionDir: String;
  55. Protected
  56. Procedure CheckSession;
  57. Function GetSessionID : String; override;
  58. Function GetSessionVariable(VarName : String) : String; override;
  59. procedure SetSessionVariable(VarName : String; const AValue: String); override;
  60. Property Cached : Boolean Read FCached Write FCached;
  61. property SessionCookie : String Read FSessionCookie Write FSessionCookie;
  62. Property SessionDir : String Read GetSessionDir Write FSessionDir;
  63. Property SessionCookiePath : String Read FSessionCookiePath write FSessionCookiePath;
  64. Public
  65. Destructor Destroy; override;
  66. Procedure Terminate; override;
  67. Procedure UpdateResponse(AResponse : TResponse); override;
  68. Procedure InitSession(ARequest : TRequest; OnNewSession, OnExpired: TNotifyEvent); override;
  69. Procedure InitResponse(AResponse : TResponse); override;
  70. Procedure RemoveVariable(VariableName : String); override;
  71. end;
  72. TFPWebSession = Class(TIniWebSession)
  73. Public
  74. Property Cached;
  75. property SessionCookie;
  76. Property SessionCookiePath;
  77. Property SessionDir;
  78. end;
  79. EWebSessionError = Class(HTTPError);
  80. TGetSessionEvent = Procedure(Var ASession : TCustomSession) of object;
  81. Var
  82. GlobalSessionDir : String;
  83. OnGetDefaultSession : TGetSessionEvent;
  84. Function GetDefaultSession : TCustomSession;
  85. implementation
  86. {$ifdef cgidebug}
  87. uses dbugintf;
  88. {$endif}
  89. Const
  90. // Sections in ini file
  91. SSession = 'Session';
  92. SData = 'Data';
  93. KeyStart = 'Start'; // Start time of session
  94. KeyLast = 'Last'; // Last seen time of session
  95. KeyTimeOut = 'Timeout'; // Timeout in seconds;
  96. SFPWebSession = 'FPWebSession'; // Cookie name for session.
  97. resourcestring
  98. SErrSessionTerminated = 'No web session active: Session was terminated';
  99. SErrNoSession = 'No web session active: Session was not started';
  100. Function GetDefaultSession : TCustomSession;
  101. begin
  102. {$ifdef cgidebug}SendMethodEnter('GetDefaultSession');{$endif}
  103. Result:=Nil;
  104. If (GlobalSessionDir='') then
  105. GlobalSessionDir:=IncludeTrailingPathDelimiter(GetTempDir(True))
  106. else
  107. GlobalSessionDir:=IncludeTrailingPathDelimiter(GlobalSessionDir);
  108. {$ifdef cgidebug}SendDebug('GetDefaultSession, session dir: '+GlobalSessionDir);{$endif}
  109. If Assigned(OnGetDefaultSession) then
  110. OnGetDefaultSession(Result);
  111. if (Result=Nil) then
  112. begin
  113. {$ifdef cgidebug}Senddebug('Creating iniwebsession');{$endif}
  114. Result:=TFPWebSession.Create(Nil);
  115. end;
  116. {$ifdef cgidebug}SendMethodExit('GetDefaultSession');{$endif}
  117. end;
  118. { TIniWebSession }
  119. function TIniWebSession.GetSessionID: String;
  120. begin
  121. If (SID='') then
  122. SID:=inherited GetSessionID;
  123. Result:=SID;
  124. end;
  125. procedure TIniWebSession.FreeIniFile;
  126. begin
  127. If Cached and Assigned(FIniFile) then
  128. TMemIniFile(FIniFile).UpdateFile;
  129. FreeAndNil(FIniFile);
  130. end;
  131. function TIniWebSession.GetSessionDir: String;
  132. begin
  133. Result:=FSessionDir;
  134. If (Result='') then
  135. Result:=GlobalSessionDir;
  136. end;
  137. Procedure TIniWebSession.CheckSession;
  138. begin
  139. If Not Assigned(FInifile) then
  140. if FTerminated then
  141. Raise EWebSessionError.Create(SErrSessionTerminated)
  142. else
  143. Raise EWebSessionError.Create(SErrNoSession)
  144. end;
  145. function TIniWebSession.GetSessionVariable(VarName: String): String;
  146. begin
  147. CheckSession;
  148. Result:=FIniFile.ReadString(SData,VarName,'');
  149. end;
  150. procedure TIniWebSession.SetSessionVariable(VarName: String;
  151. const AValue: String);
  152. begin
  153. CheckSession;
  154. FIniFile.WriteString(SData,VarName,AValue);
  155. If Not Cached then
  156. TMemIniFile(FIniFile).UpdateFile;
  157. end;
  158. destructor TIniWebSession.Destroy;
  159. begin
  160. // In case an exception occured and UpdateResponse is not called,
  161. // write the updates to disk and free FIniFile
  162. FreeIniFile;
  163. inherited Destroy;
  164. end;
  165. procedure TIniWebSession.Terminate;
  166. begin
  167. FTerminated:=True;
  168. If Assigned(FIniFile) Then
  169. begin
  170. DeleteFile(Finifile.FileName);
  171. FreeAndNil(FIniFile);
  172. end;
  173. end;
  174. procedure TIniWebSession.UpdateResponse(AResponse: TResponse);
  175. begin
  176. // Do nothing. Init has done the job.
  177. FreeIniFile;
  178. end;
  179. procedure TIniWebSession.InitSession(ARequest: TRequest; OnNewSession,OnExpired: TNotifyEvent);
  180. Var
  181. L,D : TDateTime;
  182. T : Integer;
  183. S : String;
  184. begin
  185. {$ifdef cgidebug}SendMethodEnter('TIniWebSession.InitSession');{$endif}
  186. // First initialize all session-dependent properties to their default, because
  187. // in Apache-modules or fcgi programs the session-instance is re-used
  188. SID := '';
  189. FSessionStarted := False;
  190. FTerminated := False;
  191. // If a exception occured during a prior request FIniFile is still not freed
  192. if assigned(FIniFile) then FreeIniFile;
  193. If (SessionCookie='') then
  194. SessionCookie:=SFPWebSession;
  195. S:=ARequest.CookieFields.Values[SessionCookie];
  196. // have session cookie ?
  197. If (S<>'') then
  198. begin
  199. {$ifdef cgidebug}SendDebug('Reading ini file:'+S);{$endif}
  200. FiniFile:=TMemIniFile.Create(IncludeTrailingPathDelimiter(SessionDir)+S);
  201. L:=Finifile.ReadDateTime(SSession,KeyLast,0);
  202. {$ifdef cgidebug}
  203. If (L=0) then
  204. SendDebug('No datetime in inifile (or not valid datetime : '+Finifile.ReadString(SSession,KeyLast,''));
  205. {$endif}
  206. T:=FIniFile.ReadInteger(SSession,KeyTimeOut,Self.TimeOutMinutes);
  207. {$ifdef cgidebug}SendDebug('Timeout :'+IntToStr(t));{$endif}
  208. {$ifdef cgidebug}SendDebug('Last :'+FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz',L));{$endif}
  209. If ((Now-L)>(T/(24*60))) then
  210. begin
  211. {$ifdef cgidebug}SendDebug('Timeout :'+FloatToStr(T/(24*60)));{$endif}
  212. {$ifdef cgidebug}SendDebug('Timeout :'+FormatDateTime('hh:nn:ss.zzz',(T/(24*60))));{$endif}
  213. {$ifdef cgidebug}SendDebug('Diff :'+FormatDateTime('hh:nn:ss.zzz',Now-L));{$endif}
  214. {$ifdef cgidebug}SendDebug('Ini file session expired: '+S);{$endif}
  215. // Expire session.
  216. If Assigned(OnExpired) then
  217. OnExpired(Self);
  218. DeleteFile(FIniFIle.FileName);
  219. FreeAndNil(FInifile);
  220. S:='';
  221. end
  222. else
  223. SID:=S;
  224. end;
  225. If (S='') then
  226. begin
  227. If Assigned(OnNewSession) then
  228. OnNewSession(Self);
  229. GetSessionID;
  230. S:=IncludeTrailingPathDelimiter(SessionDir)+SessionID;
  231. {$ifdef cgidebug}SendDebug('Creating new Ini file : '+S);{$endif}
  232. FIniFile:=TMemIniFile.Create(S);
  233. FIniFile.WriteDateTime(SSession,KeyStart,Now);
  234. FIniFile.WriteInteger(SSession,KeyTimeOut,Self.TimeOutMinutes);
  235. FSessionStarted:=True;
  236. end;
  237. FIniFile.WriteDateTime(SSession,KeyLast,Now);
  238. If not FCached then
  239. FIniFile.UpdateFile;
  240. {$ifdef cgidebug}SendMethodExit('TIniWebSession.InitSession');{$endif}
  241. end;
  242. procedure TIniWebSession.InitResponse(AResponse: TResponse);
  243. Var
  244. C : TCookie;
  245. begin
  246. {$ifdef cgidebug}SendMethodEnter('TIniWebSession.InitResponse');{$endif}
  247. If FSessionStarted then
  248. begin
  249. {$ifdef cgidebug}SendDebug('Session started');{$endif}
  250. C:=AResponse.Cookies.FindCookie(SessionCookie);
  251. If (C=Nil) then
  252. begin
  253. C:=AResponse.Cookies.Add;
  254. C.Name:=SessionCookie;
  255. end;
  256. C.Value:=SID;
  257. C.Path:=FSessionCookiePath;
  258. end
  259. else If FTerminated then
  260. begin
  261. {$ifdef cgidebug}SendDebug('Session terminated');{$endif}
  262. C:=AResponse.Cookies.Add;
  263. C.Name:=SessionCookie;
  264. C.Value:='';
  265. end;
  266. {$ifdef cgidebug}SendMethodExit('TIniWebSession.InitResponse');{$endif}
  267. end;
  268. procedure TIniWebSession.RemoveVariable(VariableName: String);
  269. begin
  270. {$ifdef cgidebug}SendMethodEnter('TIniWebSession.RemoveVariable');{$endif}
  271. CheckSession;
  272. FIniFile.DeleteKey(SData,VariableName);
  273. If Not Cached then
  274. TMemIniFile(FIniFile).UpdateFile;
  275. {$ifdef cgidebug}SendMethodExit('TIniWebSession.RemoveVariable');{$endif}
  276. end;
  277. function TSessionHTTPModule.GetSession: TCustomSession;
  278. begin
  279. {$ifdef cgidebug}SendMethodEnter('SessionHTTPModule.GetSession');{$endif}
  280. If (csDesigning in ComponentState) then
  281. begin
  282. {$ifdef cgidebug}SendDebug('Sending session');{$endif}
  283. Result:=FSession
  284. end
  285. else
  286. begin
  287. If (FSession=Nil) then
  288. begin
  289. {$ifdef cgidebug}SendDebug('Getting default session');{$endif}
  290. FSession:=GetDefaultSession;
  291. FSession.FreeNotification(Self);
  292. end;
  293. Result:=FSession
  294. end;
  295. {$ifdef cgidebug}SendMethodExit('SessionHTTPModule.GetSession');{$endif}
  296. end;
  297. procedure TSessionHTTPModule.SetSession(const AValue: TCustomSession);
  298. begin
  299. if FSession<>AValue then
  300. begin
  301. If Assigned(FSession) then
  302. FSession.RemoveFreeNotification(Self);
  303. FSession:=AValue;
  304. If Assigned(FSession) then
  305. FSession.FreeNotification(Self);
  306. end;
  307. end;
  308. procedure TSessionHTTPModule.CheckSession(ARequest : TRequest);
  309. begin
  310. {$ifdef cgidebug}SendMethodEnter('SessionHTTPModule('+Name+').CheckSession');{$endif}
  311. If CreateSession then
  312. begin
  313. If (FSession=Nil) then
  314. FSession:=GetDefaultSession;
  315. if Assigned(FSession) then
  316. FSession.InitSession(ARequest,FOnNewSession,FOnSessionExpired);
  317. end;
  318. {$ifdef cgidebug}SendMethodExit('SessionHTTPModule('+Name+').CheckSession');{$endif}
  319. end;
  320. procedure TSessionHTTPModule.InitSession(AResponse: TResponse);
  321. begin
  322. {$ifdef cgidebug}SendMethodEnter('SessionHTTPModule('+Name+').InitSession');{$endif}
  323. If CreateSession and Assigned(FSession) then
  324. FSession.InitResponse(AResponse);
  325. {$ifdef cgidebug}SendMethodExit('SessionHTTPModule('+Name+').InitSession');{$endif}
  326. end;
  327. procedure TSessionHTTPModule.UpdateSession(AResponse: TResponse);
  328. begin
  329. If CreateSession And Assigned(FSession) then
  330. FSession.UpdateResponse(AResponse);
  331. end;
  332. procedure TSessionHTTPModule.DoneSession;
  333. begin
  334. FreeAndNil(FSession);
  335. end;
  336. destructor TSessionHTTPModule.destroy;
  337. begin
  338. // Prevent memory leaks.
  339. If Assigned(FSession) then
  340. DoneSession;
  341. inherited destroy;
  342. end;
  343. procedure TSessionHTTPModule.Notification(AComponent: TComponent;
  344. Operation: TOperation);
  345. begin
  346. {$ifdef cgidebug}SendMethodEnter('SessionHTTPModule('+Name+').Notification');{$endif}
  347. inherited Notification(AComponent, Operation);
  348. If (Operation=opRemove) then
  349. if (AComponent=FSession) Then
  350. FSession:=Nil;
  351. {$ifdef cgidebug}SendMethodExit('SessionHTTPModule('+Name+').Notification');{$endif}
  352. end;
  353. procedure TSessionHTTPModule.Loaded;
  354. begin
  355. {$ifdef cgidebug}SendMethodEnter('SessionHTTPModule.Loaded');{$endif}
  356. inherited Loaded;
  357. If CreateSession And (FSession=Nil) then
  358. FSession:=GetDefaultSession;
  359. {$ifdef cgidebug}SendMethodExit('SessionHTTPModule.Loaded');{$endif}
  360. end;
  361. end.