IdCookieManager.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.5 2004.10.27 9:17:46 AM czhower
  18. For TIdStrings
  19. Rev 1.4 7/28/04 11:43:32 PM RLebeau
  20. Bug fix for CleanupCookieList()
  21. Rev 1.3 2004.02.03 5:45:02 PM czhower
  22. Name changes
  23. Rev 1.2 1/22/2004 7:10:02 AM JPMugaas
  24. Tried to fix AnsiSameText depreciation.
  25. Rev 1.1 2004.01.21 1:04:54 PM czhower
  26. InitComponenet
  27. Rev 1.0 11/14/2002 02:16:26 PM JPMugaas
  28. 2001-Mar-31 Doychin Bondzhev
  29. - Added new method AddCookie2 that is called when we have Set-Cookie2 as response
  30. - The common code in AddCookie and AddCookie2 is now in DoAdd
  31. 2001-Mar-24 Doychin Bondzhev
  32. - Added OnNewCookie event
  33. This event is called for every new cookie. Can be used to ask the user program
  34. do we have to store this cookie in the cookie collection
  35. - Added new method AddCookie
  36. This calls the OnNewCookie event and if the result is true it adds the new cookie
  37. in the collection
  38. }
  39. unit IdCookieManager;
  40. {
  41. Implementation of the HTTP State Management Mechanism as specified in RFC 6265.
  42. Author: Remy Lebeau ([email protected])
  43. Copyright: (c) Chad Z. Hower and The Indy Team.
  44. }
  45. interface
  46. {$i IdCompilerDefines.inc}
  47. uses
  48. Classes,
  49. IdBaseComponent,
  50. IdCookie,
  51. IdHeaderList,
  52. IdURI;
  53. Type
  54. TOnNewCookieEvent = procedure(ASender: TObject; ACookie: TIdCookie; var VAccept: Boolean) of object;
  55. TOnCookieManagerEvent = procedure(ASender: TObject; ACookieCollection: TIdCookies) of object;
  56. TOnCookieCreateEvent = TOnCookieManagerEvent;
  57. TOnCookieDestroyEvent = TOnCookieManagerEvent;
  58. TIdCookieManager = class(TIdBaseComponent)
  59. protected
  60. FOnCreate: TOnCookieCreateEvent;
  61. FOnDestroy: TOnCookieDestroyEvent;
  62. FOnNewCookie: TOnNewCookieEvent;
  63. FCookieCollection: TIdCookies;
  64. procedure CleanupCookieList;
  65. procedure DoOnCreate; virtual;
  66. procedure DoOnDestroy; virtual;
  67. function DoOnNewCookie(ACookie: TIdCookie): Boolean; virtual;
  68. procedure InitComponent; override;
  69. public
  70. destructor Destroy; override;
  71. //
  72. procedure AddServerCookie(const ACookie: String; AURL: TIdURI);
  73. procedure AddServerCookies(const ACookies: TStrings; AURL: TIdURI);
  74. procedure AddCookies(ASource: TIdCookieManager);
  75. procedure CopyCookie(ACookie: TIdCookie);
  76. //
  77. procedure GenerateClientCookies(AURL: TIdURI; SecureOnly: Boolean;
  78. Headers: TIdHeaderList);
  79. //
  80. property CookieCollection: TIdCookies read FCookieCollection;
  81. published
  82. property OnCreate: TOnCookieCreateEvent read FOnCreate write FOnCreate;
  83. property OnDestroy: TOnCookieDestroyEvent read FOnDestroy write FOnDestroy;
  84. property OnNewCookie: TOnNewCookieEvent read FOnNewCookie write FOnNewCookie;
  85. end;
  86. //procedure SplitCookies(const ACookie: String; ACookies: TStrings);
  87. implementation
  88. uses
  89. {$IFDEF HAS_UNIT_Generics_Defaults}
  90. System.Generics.Defaults,
  91. {$ENDIF}
  92. IdGlobal, IdGlobalProtocols, SysUtils;
  93. { TIdCookieManager }
  94. destructor TIdCookieManager.Destroy;
  95. begin
  96. CleanupCookieList;
  97. DoOnDestroy;
  98. FreeAndNil(FCookieCollection);
  99. inherited Destroy;
  100. end;
  101. function SortCookiesFunc({$IFDEF HAS_GENERICS_TList}const {$ENDIF}Item1, Item2: TIdCookie): Integer;
  102. begin
  103. // using the algorithm defined in RFC 6265 section 5.4
  104. if Item1 = Item2 then
  105. begin
  106. Result := 0;
  107. end
  108. else if Length(Item2.Path) > Length(Item1.Path) then
  109. begin
  110. Result := 1;
  111. end
  112. else if Length(Item1.Path) = Length(Item2.Path) then
  113. begin
  114. if Item2.CreatedAt < Item1.CreatedAt then begin
  115. Result := 1;
  116. end else begin
  117. Result := -1;
  118. end;
  119. end else
  120. begin
  121. Result := -1;
  122. end;
  123. end;
  124. procedure TIdCookieManager.GenerateClientCookies(AURL: TIdURI; SecureOnly: Boolean;
  125. Headers: TIdHeaderList);
  126. var
  127. I: Integer;
  128. LCookieList: TIdCookieList;
  129. LResultList: TIdCookieList;
  130. LCookie: TIdCookie;
  131. LCookiesToSend: String;
  132. LNow: TDateTime;
  133. begin
  134. // check for expired cookies first...
  135. CleanupCookieList;
  136. LCookieList := CookieCollection.LockCookieList(caRead);
  137. try
  138. if LCookieList.Count > 0 then begin
  139. LResultList := TIdCookieList.Create;
  140. try
  141. // Search for cookies for this domain and URI
  142. for I := 0 to LCookieList.Count-1 do begin
  143. LCookie := LCookieList[I];
  144. if LCookie.IsAllowed(AURL, SecureOnly) then begin
  145. LResultList.Add(LCookie);
  146. end;
  147. end;
  148. if LResultList.Count > 0 then begin
  149. if LResultList.Count > 1 then begin
  150. LResultList.Sort(
  151. {$IFDEF HAS_GENERICS_TList}
  152. TComparer<TIdCookie>.Construct(SortCookiesFunc)
  153. {$ELSE}
  154. TListSortCompare(@SortCookiesFunc)
  155. {$ENDIF}
  156. );
  157. end;
  158. LNow := Now;
  159. for I := 0 to LResultList.Count-1 do begin
  160. LResultList[I].LastAccessed := LNow;
  161. end;
  162. LCookiesToSend := LResultList[0].ClientCookie;
  163. for I := 1 to LResultList.Count-1 do begin
  164. LCookiesToSend := LCookiesToSend + '; ' + LResultList[I].ClientCookie; {Do not Localize}
  165. end;
  166. Headers.AddValue('Cookie', LCookiesToSend); {Do not Localize}
  167. end;
  168. finally
  169. LResultList.Free;
  170. end;
  171. end;
  172. finally
  173. CookieCollection.UnlockCookieList(caRead);
  174. end;
  175. end;
  176. procedure TIdCookieManager.AddServerCookie(const ACookie: String; AURL: TIdURI);
  177. var
  178. LCookie: TIdCookie;
  179. begin
  180. // TODO: use TIdCookies.AddServerCookie() after adding
  181. // a way for it to query the manager for rejections...
  182. //
  183. //FCookieCollection.AddServerCookie(ACookie, AURI);
  184. LCookie := FCookieCollection.Add;
  185. try
  186. if LCookie.ParseServerCookie(ACookie, AURL) then
  187. begin
  188. if DoOnNewCookie(LCookie) then
  189. begin
  190. if FCookieCollection.AddCookie(LCookie, AURL) then begin
  191. LCookie := nil;
  192. Exit;
  193. end;
  194. end;
  195. end;
  196. finally
  197. if LCookie <> nil then
  198. begin
  199. LCookie.Collection := nil;
  200. LCookie.Free;
  201. end;
  202. end;
  203. end;
  204. procedure TIdCookieManager.AddCookies(ASource: TIdCookieManager);
  205. begin
  206. if (ASource <> nil) and (ASource <> Self) then begin
  207. FCookieCollection.AddCookies(ASource.CookieCollection);
  208. end;
  209. end;
  210. procedure TIdCookieManager.AddServerCookies(const ACookies: TStrings; AURL: TIdURI);
  211. var
  212. I: Integer;
  213. begin
  214. for I := 0 to ACookies.Count-1 do begin
  215. AddServerCookie(ACookies[I], AURL);
  216. end;
  217. end;
  218. procedure TIdCookieManager.CopyCookie(ACookie: TIdCookie);
  219. var
  220. LCookie: TIdCookie;
  221. begin
  222. LCookie := TIdCookieClass(ACookie.ClassType).Create(FCookieCollection);
  223. try
  224. LCookie.Assign(ACookie);
  225. if LCookie.Domain <> '' then
  226. begin
  227. if DoOnNewCookie(LCookie) then
  228. begin
  229. if FCookieCollection.AddCookie(LCookie, nil) then begin
  230. LCookie := nil;
  231. end;
  232. end;
  233. end;
  234. finally
  235. if LCookie <> nil then
  236. begin
  237. LCookie.Collection := nil;
  238. LCookie.Free;
  239. end;
  240. end;
  241. end;
  242. function TIdCookieManager.DoOnNewCookie(ACookie: TIdCookie): Boolean;
  243. begin
  244. Result := True;
  245. if Assigned(FOnNewCookie) then begin
  246. OnNewCookie(Self, ACookie, Result);
  247. end;
  248. end;
  249. procedure TIdCookieManager.DoOnCreate;
  250. begin
  251. if Assigned(FOnCreate) then begin
  252. OnCreate(Self, FCookieCollection);
  253. end;
  254. end;
  255. procedure TIdCookieManager.DoOnDestroy;
  256. begin
  257. if Assigned(FOnDestroy) then
  258. begin
  259. OnDestroy(Self, FCookieCollection);
  260. end;
  261. end;
  262. procedure TIdCookieManager.CleanupCookieList;
  263. var
  264. i: Integer;
  265. LCookieList: TIdCookieList;
  266. LCookie: TIdCookie;
  267. begin
  268. LCookieList := FCookieCollection.LockCookieList(caReadWrite);
  269. try
  270. for i := LCookieList.Count-1 downto 0 do
  271. begin
  272. LCookie := LCookieList[i];
  273. if LCookie.IsExpired then
  274. begin
  275. // The Cookie has expired. It has to be removed from the collection
  276. LCookieList.Delete(i);
  277. // must set the Collection to nil or the cookie will try to remove
  278. // itself from the cookie collection and deadlock
  279. LCookie.Collection := nil;
  280. LCookie.Free;
  281. end;
  282. end;
  283. finally
  284. FCookieCollection.UnlockCookieList(caReadWrite);
  285. end;
  286. end;
  287. procedure TIdCookieManager.InitComponent;
  288. begin
  289. inherited InitComponent;
  290. FCookieCollection := TIdCookies.Create(Self);
  291. DoOnCreate;
  292. end;
  293. end.