webmodule.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. unit webmodule;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. SysUtils, Classes, httpdefs, fpHTTP, fpWeb, iniwebsession;
  6. type
  7. { TFPWebModule1 }
  8. TFPWebModule1 = class(TFPWebModule)
  9. procedure DataModuleAfterResponse(Sender: TObject; AResponse: TResponse);
  10. procedure DataModuleCreate(Sender: TObject);
  11. //web action handlers
  12. procedure loginRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
  13. procedure logoutRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
  14. procedure someactionRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
  15. private
  16. { private declarations }
  17. MySessionDir : String;
  18. LoggedInLoginName : String;
  19. SessionDBFile : String;
  20. UserDBFile : String;
  21. function NotLoggedIn:Boolean;
  22. function CommonTemplateTagReplaces(const TagString:String; TagParams: TStringList; out ReplaceText: String):Boolean;
  23. //template tag handlers
  24. procedure loginReplaceTag(Sender: TObject; const TagString: String; TagParams: TStringList; Out ReplaceText: String);
  25. procedure welcomeReplaceTag(Sender: TObject; const TagString:String; TagParams: TStringList; Out ReplaceText: String);
  26. procedure logoutReplaceTag(Sender: TObject; const TagString:String; TagParams: TStringList; Out ReplaceText: String);
  27. procedure someactionReplaceTag(Sender: TObject; const TagString:String; TagParams: TStringList; Out ReplaceText: String);
  28. public
  29. { public declarations }
  30. end;
  31. var
  32. FPWebModule1: TFPWebModule1;
  33. implementation
  34. {$R *.lfm}
  35. { TFPWebModule1 }
  36. function FindNameInList(const SL:TStrings; const N:String):String;
  37. var
  38. i : Integer;
  39. begin
  40. Result := '';
  41. for i := 0 to SL.Count - 1 do
  42. if SL.Names[i] = N then
  43. begin
  44. Result := SL.Values[SL.Names[i]];//return with the sessionID
  45. break;
  46. end;
  47. end;
  48. procedure RemoveValueIfExists(SL:TStrings; const S_ID:String);
  49. var
  50. s : String;
  51. i : Integer;
  52. begin
  53. if SL.Count <= 0 then Exit;
  54. s := '=' + S_ID;
  55. i := 0;
  56. repeat
  57. if pos(s, SL[i]) > 0 then
  58. SL.Delete(i)
  59. else
  60. inc(i);
  61. until i >= SL.Count;
  62. end;
  63. function FindValueInList(const SL:TStrings; const Sess:String):String;
  64. var
  65. s : String;
  66. i : Integer;
  67. begin
  68. Result := '';
  69. if SL.Count <= 0 then Exit;
  70. s := '=' + Sess;
  71. i := 0;
  72. repeat
  73. if pos(s, SL[i]) > 0 then
  74. begin
  75. Result := SL.Names[i];
  76. break;
  77. end;
  78. inc(i);
  79. until i >= SL.Count;
  80. end;
  81. procedure RemoveNameIfExists(SL:TStrings; const N:String);
  82. var
  83. i: Integer;
  84. begin
  85. if SL.Count <= 0 then Exit;
  86. i := 0;
  87. repeat
  88. if SL.Names[i] = N then
  89. SL.Delete(i)
  90. else
  91. inc(i);
  92. until i >= SL.Count;
  93. end;
  94. procedure TFPWebModule1.DataModuleAfterResponse(Sender: TObject;
  95. AResponse: TResponse);
  96. begin
  97. //reset global variables for apache modules and fcgi applications for the next incoming request
  98. LoggedInLoginName := '';
  99. //
  100. end;
  101. procedure TFPWebModule1.DataModuleCreate(Sender: TObject);
  102. begin
  103. ModuleTemplate.AllowTagParams := true;
  104. ModuleTemplate.StartDelimiter := '{+'; //The default is { and } which is usually not good if we use Javascript in our templates
  105. ModuleTemplate.EndDelimiter := '+}';
  106. CreateSession := true; //Turn on automatic session handling for this web module
  107. MySessionDir := '';//'/Path/To/A/Directory/';{Use this if you don't want the automatic Temp dir to store the sessionID files under "fpwebsessions" sub-directory}
  108. with (SessionFactory as TIniSessionFactory) do
  109. begin
  110. DefaultTimeoutMinutes := 2; //Session timeout in minutes
  111. SessionDir := MySessionDir;
  112. // SessionCookie:='ACustomCookieName'; {Use this to set the cookie name that will be used for the session management. Default is 'FPWebSession'}
  113. end;
  114. sessiondbfile := 'sessiondb.txt'; //This will contain the name=sessionID pairs to simulate the session database
  115. userdbfile := 'userdb.txt'; //This simulates a user database with passwords
  116. end;
  117. procedure TFPWebModule1.loginRequest(Sender: TObject; ARequest: TRequest;
  118. AResponse: TResponse; var Handled: Boolean);
  119. var
  120. loginname, pwd, pwd1 : String;
  121. userdatabase, sessiondatabase : TStringlist;
  122. begin
  123. Handled := true;
  124. ModuleTemplate.FileName := 'testlogin.html';
  125. ModuleTemplate.OnReplaceTag := @loginReplaceTag;
  126. AResponse.CustomHeaders.Add('Pragma=no-cache');//do not cache the response in the web browser so the Back key can not be used to see the pages without reloads.
  127. if FindNameInList(ARequest.ContentFields, 'LoginName') = '' then
  128. begin//called the login action without parameters -> display the login page
  129. ARequest.QueryFields.Add('MSG=NORMAL');
  130. AResponse.Content := ModuleTemplate.GetContent;
  131. Exit;
  132. end;
  133. loginname := Trim(ARequest.ContentFields.Values['LoginName']);
  134. pwd := Trim(ARequest.ContentFields.Values['Password']);
  135. if (pwd = '') or (loginname = '') then
  136. begin//empty login name or password -> return to the login screen
  137. ARequest.QueryFields.Add('MSG=MISSING');
  138. AResponse.Content := ModuleTemplate.GetContent;
  139. Exit;
  140. end;
  141. //simulate a user database loaded into a stringlist
  142. userdatabase := TStringlist.Create;
  143. userdatabase.LoadFromFile(userdbfile);
  144. pwd1 := userdatabase.Values[LoginName];//get the correct password for the LoginName if it is there
  145. userdatabase.free;
  146. //
  147. if pwd <> pwd1 then
  148. begin//either the password or the login name was invalid
  149. ARequest.QueryFields.Add('MSG=INVLOGIN');
  150. AResponse.Content := ModuleTemplate.GetContent;
  151. Exit;
  152. end;
  153. //successful login
  154. LoggedInLoginName := loginname;
  155. //session starting, need to store it somewhere next to the name of the logged in person
  156. sessiondatabase := TStringList.Create;
  157. if FileExists(sessiondbfile) then
  158. sessiondatabase.LoadFromFile(sessiondbfile); //simulating the session database access
  159. if sessiondatabase.Count > 0 then
  160. RemoveValueIfExists(sessiondatabase, Session.SessionID); //New login, kill all sessions with this session ID (same computer, same browser, multiple persons)
  161. if FindNameInList(sessiondatabase, LoginName) <> '' then
  162. sessiondatabase.Values[LoginName] := Session.SessionID //overwrite with the new session ID
  163. else
  164. sessiondatabase.Add(LoginName + '=' + Session.SessionID); //create a new entry for this person
  165. sessiondatabase.SaveToFile(sessiondbfile); //simulating the session database update
  166. sessiondatabase.Free;
  167. //generate the Welcome page content
  168. ModuleTemplate.FileName := 'testwelcome.html';
  169. ModuleTemplate.OnReplaceTag := @welcomeReplaceTag;
  170. AResponse.Content := ModuleTemplate.GetContent;
  171. end;
  172. procedure TFPWebModule1.loginReplaceTag(Sender: TObject; const TagString:
  173. String; TagParams: TStringList; Out ReplaceText: String);
  174. begin
  175. {Handle tags used in multiple templates}
  176. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  177. Exit;
  178. {Handle tags specific to this template if there are any}
  179. if AnsiCompareText(TagString, 'MESSAGE') = 0 then
  180. begin
  181. ReplaceText := TagParams.Values[Request.QueryFields.Values['MSG']];
  182. end else
  183. {Message for tags not handled}
  184. begin
  185. ReplaceText := '[Template tag "' + TagString + '" is not implemented yet.]';
  186. end;
  187. end;
  188. procedure TFPWebModule1.welcomeReplaceTag(Sender: TObject; const TagString:String;
  189. TagParams: TStringList; Out ReplaceText: String);
  190. begin
  191. {Handle tags used in multiple templates}
  192. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  193. Exit;
  194. {Handle tags specific to this template if there are any}
  195. {Message for tags not handled}
  196. begin
  197. ReplaceText := '[Template tag "' + TagString + '" is not implemented yet.]';
  198. end;
  199. end;
  200. function TFPWebModule1.CommonTemplateTagReplaces(const TagString:String;
  201. TagParams: TStringList; out ReplaceText: String):Boolean;
  202. begin
  203. Result := true;
  204. if AnsiCompareText(TagString, 'DATETIME') = 0 then
  205. begin
  206. ReplaceText := FormatDateTime(TagParams.Values['FORMAT'], Now);
  207. end else
  208. if AnsiCompareText(TagString, 'SESSIONID') = 0 then
  209. begin
  210. if Assigned(Session) then
  211. ReplaceText := Session.SessionID;
  212. end else
  213. if AnsiCompareText(TagString, 'MINUTESLEFT') = 0 then
  214. begin
  215. if Assigned(Session) then
  216. ReplaceText := IntToStr(Session.TimeOutMinutes);
  217. end else
  218. if AnsiCompareText(TagString, 'SESSIONFILE') = 0 then
  219. begin
  220. if Assigned(Session) then
  221. if MySessionDir = '' then
  222. ReplaceText := IncludeTrailingPathDelimiter(GetTempDir(True)) + IncludeTrailingPathDelimiter('fpwebsessions') + Session.SessionID
  223. else
  224. ReplaceText := IncludeTrailingPathDelimiter(MySessionDir) + Session.SessionID;
  225. {NOTE: GetTempDir
  226. used by the session manager. Returns the OS temporary directory if possible, or from the
  227. environment variable TEMP . For CGI programs you need to pass global environment
  228. variables, it is not automatic. For example in the Apache httpd.conf with a
  229. "PassEnv TEMP" or "SetEnv TEMP /pathtotmpdir" line so the web server passes this
  230. global environment variable to the CGI programs' local environment variables.
  231. }
  232. end else
  233. if AnsiCompareText(TagString, 'LOGINNAME') = 0 then
  234. begin
  235. ReplaceText := LoggedInLoginName;
  236. end else
  237. Result := false;
  238. end;
  239. function TFPWebModule1.NotLoggedIn:Boolean;
  240. var
  241. sessiondatabase : TStringlist;
  242. begin
  243. Result := false;
  244. //check if the current sessionID is in the sessionDB
  245. sessiondatabase := TStringList.Create;
  246. if FileExists(sessiondbfile) then
  247. sessiondatabase.LoadFromFile(sessiondbfile);
  248. LoggedInLoginName := FindValueInList(sessiondatabase, Session.sessionID);
  249. sessiondatabase.Free;
  250. //
  251. if LoggedInLoginName = '' then
  252. begin
  253. Result := true; //not found -> not logged in or previous session has expired
  254. //show the login screen again with the expired session message
  255. ModuleTemplate.FileName := 'testlogin.html';
  256. ModuleTemplate.OnReplaceTag := @loginReplaceTag;
  257. Request.QueryFields.Add('MSG=SESSIONEXPIRED');
  258. Response.Content := ModuleTemplate.GetContent;
  259. end;
  260. end;
  261. procedure TFPWebModule1.logoutRequest(Sender: TObject; ARequest: TRequest;
  262. AResponse: TResponse; var Handled: Boolean);
  263. var
  264. sessiondatabase : TStringList;
  265. begin
  266. Handled := true;
  267. if NotLoggedIn then Exit;
  268. //delete the sessionID and all occurences of the login name assigned to it from the sessiondb
  269. sessiondatabase := TStringList.Create;
  270. if FileExists(sessiondbfile) then
  271. sessiondatabase.LoadFromFile(sessiondbfile);
  272. if sessiondatabase.Count > 0 then
  273. begin
  274. RemoveValueIfExists(sessiondatabase, Session.SessionID);
  275. RemoveNameIfExists(sessiondatabase, LoggedInLoginName);
  276. sessiondatabase.SaveToFile(sessiondbfile);
  277. end;
  278. sessiondatabase.Free;
  279. //
  280. //terminate the session
  281. Session.Terminate;
  282. //Generate the response page
  283. ModuleTemplate.FileName := 'testlogout.html';
  284. ModuleTemplate.OnReplaceTag := @logoutReplaceTag;
  285. AResponse.Content := ModuleTemplate.GetContent;//generate the Logout page content.
  286. end;
  287. procedure TFPWebModule1.logoutReplaceTag(Sender: TObject; const TagString:String;
  288. TagParams: TStringList; Out ReplaceText: String);
  289. begin
  290. {Handle tags used in multiple templates}
  291. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  292. Exit;
  293. {Handle tags specific to this template if there are any}
  294. {Message for tags not handled}
  295. begin
  296. ReplaceText := '[Template tag "' + TagString + '" is not implemented yet.]';
  297. end;
  298. end;
  299. procedure TFPWebModule1.someactionRequest(Sender: TObject; ARequest: TRequest;
  300. AResponse: TResponse; var Handled: Boolean);
  301. begin
  302. Handled := true;
  303. if NotLoggedIn then Exit;
  304. ModuleTemplate.FileName := 'testsomepage.html';
  305. ModuleTemplate.OnReplaceTag := @someactionReplaceTag;
  306. AResponse.Content := ModuleTemplate.GetContent;//generate the testpage content
  307. end;
  308. procedure TFPWebModule1.someactionReplaceTag(Sender: TObject; const TagString:
  309. String; TagParams: TStringList; Out ReplaceText: String);
  310. begin
  311. {Handle tags used in multiple templates}
  312. if CommonTemplateTagReplaces(TagString, TagParams, ReplaceText) then
  313. Exit;
  314. {Handle tags specific to this template if there are any}
  315. {Message for tags not handled}
  316. begin
  317. ReplaceText := '[Template tag {+' + TagString + '+} is not implemented yet.]';
  318. end;
  319. end;
  320. initialization
  321. RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
  322. end.