/components/fpweb/demo/fptemplate/sessions/urlsessions-login/cgi/webmodule.pas

http://github.com/graemeg/lazarus · Pascal · 378 lines · 287 code · 59 blank · 32 comment · 30 complexity · 6b061dfcc630f728a667323f99bc9607 MD5 · raw file

  1. unit webmodule;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, FileUtil, LResources, HTTPDefs, websession, fpHTTP, fpWeb;
  6. type
  7. { TFPWebModule1 }
  8. TFPWebModule1 = class(TFPWebModule)
  9. procedure DataModuleAfterResponse(Sender: TObject; AResponse: TResponse);
  10. procedure DataModuleCreate(Sender: TObject);
  11. procedure loginRequest(Sender: TObject; ARequest: TRequest;
  12. AResponse: TResponse; var Handled: Boolean);
  13. procedure logoutRequest(Sender: TObject; ARequest: TRequest;
  14. AResponse: TResponse; var Handled: Boolean);
  15. procedure someactionRequest(Sender: TObject; ARequest: TRequest;
  16. AResponse: TResponse; var Handled: Boolean);
  17. private
  18. { private declarations }
  19. LoggedInLoginName : String;
  20. SessionID: String;
  21. SessionDBFile : String;
  22. UserDBFile : String;
  23. SessionVariable: String;
  24. TimeoutMinutes: Integer;
  25. function RemoveExpiredSessions(SL:TStringList; const SIDToDelete:String):Boolean;
  26. function NotLoggedIn:Boolean;
  27. function CommonTemplateTagReplaces(const TagString:String;
  28. TagParams: TStringList; Out ReplaceText: String):Boolean;
  29. procedure loginReplaceTag(Sender: TObject; const TagString:String;
  30. TagParams: TStringList; Out ReplaceText: String);
  31. procedure logoutReplaceTag(Sender: TObject; const TagString:String;
  32. TagParams: TStringList; Out ReplaceText: String);
  33. procedure welcomeReplaceTag(Sender: TObject; const TagString:String;
  34. TagParams: TStringList; Out ReplaceText: String);
  35. procedure someactionReplaceTag(Sender: TObject; const TagString:String;
  36. TagParams: TStringList; Out ReplaceText: String);
  37. public
  38. { public declarations }
  39. end;
  40. var
  41. FPWebModule1: TFPWebModule1;
  42. implementation
  43. { TFPWebModule1 }
  44. procedure TFPWebModule1.DataModuleAfterResponse(Sender: TObject;
  45. AResponse: TResponse);
  46. var
  47. sessiondatabase:TStringList;
  48. SIDLastRefresh:String;
  49. begin
  50. //update the session DB for the current session
  51. if (SessionID <> '')and(LoggedinLoginName <> '') then
  52. begin//for many concurrent request websites this part needs to be modified to have some kind of locking while writing into the file/relational database
  53. SIDLastRefresh := '';
  54. sessiondatabase := TStringList.Create;
  55. if FileExists(sessiondbfile) then
  56. sessiondatabase.LoadFromFile(sessiondbfile);
  57. SIDLastRefresh := sessiondatabase.Values[SessionID];
  58. if SIDLastRefresh <> '' then
  59. begin
  60. sessiondatabase.Values[SessionID] := DateTimeToStr(Now) + LoggedinLoginName;//update the Last refresh time
  61. sessiondatabase.SaveToFile(sessiondbfile);
  62. end;
  63. sessiondatabase.Free;
  64. end;
  65. //reset global variables for apache modules for the next incoming request
  66. LoggedInLoginName := '';
  67. SessionID := '';
  68. //
  69. end;
  70. procedure TFPWebModule1.DataModuleCreate(Sender: TObject);
  71. begin
  72. Template.AllowTagParams := true;
  73. Template.StartDelimiter := '{+'; //The default is { and } which is usually not good if we use Javascript in our templates
  74. Template.EndDelimiter := '+}';
  75. sessiondbfile := 'session-db.txt';//This will contain the sessionID=expiration pairs
  76. userdbfile := 'userdb.txt'; //This simulates a user database with passwords
  77. TimeOutMinutes := 2; //With a session timeout of 2 minutes
  78. SessionVariable := 'sid'; //Query parameter name for the session ID, for all links in the templates
  79. LongTimeFormat := 'hh:mm:ss'; //to save on date time conversion code
  80. ShortDateFormat := 'YYYY/MM/DD'; //to save on date time conversion code
  81. end;
  82. function FindNameInList(const SL:TStrings; const N:String):String;
  83. var
  84. i : Integer;
  85. begin
  86. Result := '';
  87. for i := 0 to SL.Count - 1 do
  88. if SL.Names[i] = N then
  89. begin
  90. Result := SL.Values[SL.Names[i]];
  91. break;
  92. end;
  93. end;
  94. function TFPWebModule1.RemoveExpiredSessions(SL:TStringList; const SIDToDelete:String):Boolean;
  95. var
  96. DT:TDateTime;
  97. i, j: Integer;
  98. s, SIDLastRefresh: String;
  99. begin
  100. Result := false;
  101. if SL.Count <= 0 then Exit;
  102. i := 0;
  103. repeat
  104. s := SL[i];
  105. j := pos('=', s);
  106. if j > 0 then
  107. begin
  108. if copy(s, 1, j - 1) = SIDToDelete then
  109. begin
  110. SL.Delete(i);
  111. dec(i);
  112. end else begin
  113. SIDLastRefresh := copy(s, j + 1, 19);{YYYY/MM/DD hh:mm:ss}
  114. DT := StrToDateTime(SIDLastRefresh);
  115. if ((Now - DT) > (TimeOutMinutes/1440)) then
  116. begin
  117. Result := true;
  118. SL.Delete(i);
  119. dec(i);
  120. end;
  121. end;
  122. end;
  123. inc(i);
  124. until i >= SL.Count;
  125. end;
  126. function TFPWebModule1.NotLoggedIn:Boolean;
  127. var
  128. sessiondatabase:TStringlist;
  129. SIDLastRefresh:String;
  130. begin
  131. Result := false;
  132. //check if the current sessionID is valid
  133. SessionID := UpperCase(Request.QueryFields.Values[SessionVariable]);
  134. if SessionID <> '' then
  135. begin
  136. sessiondatabase := TStringList.Create;
  137. if FileExists(sessiondbfile) then
  138. sessiondatabase.LoadFromFile(sessiondbfile);
  139. // if RemoveExpiredSessions(sessiondatabase, '') then //Remove all expired sessions
  140. // sessiondatabase.SaveToFile(sessiondbfile); {enough to purge only at logout events}
  141. RemoveExpiredSessions(sessiondatabase, ''); { }
  142. SIDLastRefresh := sessiondatabase.Values[SessionID];
  143. sessiondatabase.Free;
  144. if SIDLastRefresh <> '' then
  145. begin
  146. LoggedinLoginName := copy(SIDLastRefresh, 20, 1024);
  147. Exit;//OK
  148. end;
  149. end;
  150. //show the login screen again with the expired session message
  151. Template.FileName := 'testurllogin.html';
  152. Template.OnReplaceTag := @loginReplaceTag;
  153. Request.QueryFields.Add('MSG=SESSIONEXPIRED');
  154. Response.Content := Template.GetContent;
  155. Result := true;
  156. end;
  157. procedure TFPWebModule1.loginRequest(Sender: TObject; ARequest: TRequest;
  158. AResponse: TResponse; var Handled: Boolean);
  159. var
  160. loginname, pwd, pwd1 : String;
  161. userdatabase, sessiondatabase : TStringlist;
  162. G : TGUID;
  163. begin
  164. Handled := true;
  165. Template.FileName := 'testurllogin.html';
  166. Template.OnReplaceTag := @loginReplaceTag;
  167. AResponse.CustomHeaders.Add('Pragma=no-cache');//do not cache the response in the web browser
  168. if FindNameInList(ARequest.ContentFields, 'LoginName') = '' then
  169. begin//called the login action without parameters -> display the login page
  170. ARequest.QueryFields.Add('MSG=NORMAL');
  171. AResponse.Content := Template.GetContent;
  172. Exit;
  173. end;
  174. loginname := Trim(ARequest.ContentFields.Values['LoginName']);
  175. pwd := Trim(ARequest.ContentFields.Values['Password']);
  176. if (pwd = '') or (loginname = '') then
  177. begin//empty login name or password -> return to the login screen
  178. ARequest.QueryFields.Add('MSG=MISSING');
  179. AResponse.Content := Template.GetContent;
  180. Exit;
  181. end;
  182. //simulate a user database loaded into a stringlist
  183. userdatabase := TStringlist.Create;
  184. userdatabase.LoadFromFile(userdbfile);
  185. //
  186. pwd1 := userdatabase.values[LoginName];
  187. userdatabase.free;
  188. if pwd <> pwd1 then
  189. begin//either the password or the login name was invalid
  190. ARequest.QueryFields.Add('MSG=INVLOGIN');
  191. AResponse.Content := Template.GetContent;
  192. Exit;
  193. end;
  194. //succesful login
  195. LoggedInLoginName := loginname;
  196. //session starting, need to store it somewhere next to the name of the logged in person
  197. sessiondatabase := TStringList.Create;
  198. if FileExists(sessiondbfile) then
  199. sessiondatabase.LoadFromFile(sessiondbfile);
  200. CreateGUID(G);
  201. SessionID:=UpperCase(GuiDToString(G));
  202. sessiondatabase.Add(SessionID + '=' + DateTimeToStr(Now) + LoggedinLoginName);//create a new entry for this session
  203. sessiondatabase.SaveToFile(sessiondbfile);//for many concurrent request websites this part needs to be modified to have some kind of locking while writing into the file/relational database
  204. sessiondatabase.Free;
  205. //generate the Welcome page content
  206. Template.FileName := 'testurlwelcome.html';
  207. Template.OnReplaceTag := @welcomeReplaceTag;
  208. AResponse.Content := Template.GetContent;
  209. end;
  210. procedure TFPWebModule1.loginReplaceTag(Sender: TObject; const TagString:
  211. String; TagParams: TStringList; Out ReplaceText: String);
  212. begin
  213. {Handle tags used in multiple templates}
  214. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  215. Exit;
  216. {Handle tags specific to this template if there are any}
  217. if AnsiCompareText(TagString, 'MESSAGE') = 0 then
  218. begin
  219. ReplaceText := TagParams.Values[Request.QueryFields.Values['MSG']];
  220. end else
  221. {Message for tags not handled}
  222. begin
  223. ReplaceText := '[Template tag {+' + TagString + '+} is not implemented yet.]';
  224. end;
  225. end;
  226. procedure TFPWebModule1.welcomeReplaceTag(Sender: TObject; const TagString:String;
  227. TagParams: TStringList; Out ReplaceText: String);
  228. begin
  229. {Handle tags used in multiple templates}
  230. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  231. Exit;
  232. {Handle tags specific to this template if there are any}
  233. {Message for tags not handled}
  234. begin
  235. ReplaceText := '[Template tag {+' + TagString + '+} is not implemented yet.]';
  236. end;
  237. end;
  238. procedure TFPWebModule1.logoutRequest(Sender: TObject; ARequest: TRequest;
  239. AResponse: TResponse; var Handled: Boolean);
  240. var
  241. sessiondatabase : TStringList;
  242. begin
  243. Handled := true;
  244. if NotLoggedIn then Exit;
  245. //delete the sessionID from the sessiondb with all expired sessions
  246. sessiondatabase := TStringList.Create;
  247. if FileExists(sessiondbfile) then
  248. sessiondatabase.LoadFromFile(sessiondbfile);
  249. RemoveExpiredSessions(sessiondatabase, SessionID);
  250. sessiondatabase.SaveToFile(sessiondbfile);//for many concurrent request websites this part needs to be modified to have some kind of locking while writing into the file/relational database
  251. sessiondatabase.Free;
  252. //
  253. Template.FileName := 'testurllogout.html';
  254. Template.OnReplaceTag := @logoutReplaceTag;
  255. AResponse.Content := Template.GetContent;//generate the Logout page content.
  256. end;
  257. procedure TFPWebModule1.logoutReplaceTag(Sender: TObject; const TagString:String;
  258. TagParams: TStringList; Out ReplaceText: String);
  259. begin
  260. {Handle tags used in multiple templates}
  261. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  262. Exit;
  263. {Handle tags specific to this template if there are any}
  264. {Message for tags not handled}
  265. begin
  266. ReplaceText := '[Template tag {+' + TagString + '+} is not implemented yet.]';
  267. end;
  268. end;
  269. procedure TFPWebModule1.someactionRequest(Sender: TObject; ARequest: TRequest;
  270. AResponse: TResponse; var Handled: Boolean);
  271. begin
  272. Handled := true;
  273. if NotLoggedIn then Exit;
  274. Template.FileName := 'testurlsomepage.html';
  275. Template.OnReplaceTag := @someactionReplaceTag;
  276. AResponse.Content := Template.GetContent;
  277. end;
  278. procedure TFPWebModule1.someactionReplaceTag(Sender: TObject; const TagString:
  279. String; TagParams: TStringList; Out ReplaceText: String);
  280. begin
  281. {Handle tags used in multiple templates}
  282. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  283. Exit;
  284. {Handle tags specific to this template if there are any}
  285. {Message for tags not handled}
  286. begin
  287. ReplaceText := '[Template tag {+' + TagString + '+} is not implemented yet.]';
  288. end;
  289. end;
  290. function TFPWebModule1.CommonTemplateTagReplaces(const TagString:String;
  291. TagParams: TStringList; out ReplaceText: String):Boolean;
  292. begin
  293. Result := true;
  294. if AnsiCompareText(TagString, 'SESSION-VARIABLE') = 0 then
  295. begin
  296. ReplaceText := SessionVariable + '=' + SessionID;
  297. end else
  298. if AnsiCompareText(TagString, 'DATETIME') = 0 then
  299. begin
  300. ReplaceText := FormatDateTime(TagParams.Values['FORMAT'], Now);
  301. end else
  302. if AnsiCompareText(TagString, 'SESSIONID') = 0 then
  303. begin
  304. ReplaceText := SessionID;
  305. end else
  306. if AnsiCompareText(TagString, 'MINUTESLEFT') = 0 then
  307. begin
  308. ReplaceText := IntToStr(TimeOutMinutes);
  309. end else
  310. if AnsiCompareText(TagString, 'LOGINNAME') = 0 then
  311. begin
  312. ReplaceText := LoggedInLoginName;
  313. end else
  314. Result := false;
  315. end;
  316. initialization
  317. {$I webmodule.lrs}
  318. RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
  319. end.