IdSASLCollection.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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 11/27/2004 8:27:14 PM JPMugaas
  18. Fix for compiler errors.
  19. Rev 1.4 11/27/04 2:56:40 AM RLebeau
  20. Added support for overloaded version of LoginSASL().
  21. Added GetDisplayName() method to TIdSASLListEntry, and FindSASL() method to
  22. TIdSASLEntries.
  23. Rev 1.3 10/26/2004 10:55:32 PM JPMugaas
  24. Updated refs.
  25. Rev 1.2 6/11/2004 9:38:38 AM DSiders
  26. Added "Do not Localize" comments.
  27. Rev 1.1 2004.02.03 5:45:50 PM czhower
  28. Name changes
  29. Rev 1.0 1/25/2004 3:09:54 PM JPMugaas
  30. New collection class for SASL mechanism processing.
  31. }
  32. unit IdSASLCollection;
  33. interface
  34. {$i IdCompilerDefines.inc}
  35. uses
  36. Classes,
  37. IdBaseComponent,
  38. IdCoder,
  39. IdException,
  40. IdGlobal,
  41. IdSASL,
  42. IdTCPConnection;
  43. type
  44. TIdSASLEntries = class;
  45. TIdSASLListEntry = class(TCollectionItem)
  46. protected
  47. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FSASL : TIdSASL;
  48. function GetDisplayName: String; override;
  49. function GetOwnerComponent: TComponent;
  50. function GetSASLEntries: TIdSASLEntries;
  51. procedure SetSASL(AValue : TIdSASL);
  52. public
  53. procedure Assign(Source: TPersistent); override;
  54. property OwnerComponent: TComponent read GetOwnerComponent;
  55. property SASLEntries: TIdSASLEntries read GetSASLEntries;
  56. published
  57. property SASL : TIdSASL read FSASL write SetSASL;
  58. end;
  59. TIdSASLEntries = class ( TOwnedCollection )
  60. protected
  61. procedure CheckIfEmpty;
  62. function GetItem(Index: Integer) : TIdSASLListEntry;
  63. function GetOwnerComponent: TComponent;
  64. procedure SetItem(Index: Integer; const Value: TIdSASLListEntry);
  65. public
  66. constructor Create ( AOwner : TPersistent ); reintroduce;
  67. function Add: TIdSASLListEntry;
  68. procedure LoginSASL(const ACmd, AHost, AProtocolName: String;
  69. const AOkReplies, AContinueReplies: array of string; AClient : TIdTCPConnection;
  70. ACapaReply : TStrings; const AAuthString : String = 'AUTH'; {Do not Localize}
  71. ACanAttemptIR: Boolean = True); overload; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use overload with APort parameter'{$ENDIF};{$ENDIF}
  72. procedure LoginSASL(const ACmd, AHost: String; const APort: TIdPort; const AProtocolName: String;
  73. const AOkReplies, AContinueReplies: array of string; AClient : TIdTCPConnection;
  74. ACapaReply : TStrings; const AAuthString : String = 'AUTH'; {Do not Localize}
  75. ACanAttemptIR: Boolean = True); overload;
  76. procedure LoginSASL(const ACmd, AHost, AProtocolName, AServiceName: String;
  77. const AOkReplies, AContinueReplies: array of string; AClient : TIdTCPConnection;
  78. ACapaReply : TStrings; const AAuthString : String = 'AUTH'; {Do not Localize}
  79. ACanAttemptIR: Boolean = True); overload; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use overload with APort parameter'{$ENDIF};{$ENDIF}
  80. procedure LoginSASL(const ACmd, AHost: String; const APort: TIdPort; const AProtocolName, AServiceName: String;
  81. const AOkReplies, AContinueReplies: array of string; AClient : TIdTCPConnection;
  82. ACapaReply : TStrings; const AAuthString : String = 'AUTH'; {Do not Localize}
  83. ACanAttemptIR: Boolean = True); overload;
  84. function ParseCapaReply(ACapaReply: TStrings; const AAuthString: String = 'AUTH') : TStrings; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ParseCapaReplyToList()'{$ENDIF};{$ENDIF} {do not localize}
  85. procedure ParseCapaReplyToList(ACapaReply, ADestList: TStrings; const AAuthString: String = 'AUTH'); {do not localize}
  86. function FindSASL(const AServiceName: String): TIdSASL;
  87. function Insert(Index: Integer): TIdSASLListEntry;
  88. procedure RemoveByComp(AComponent : TComponent);
  89. function IndexOfComp(AItem : TIdSASL): Integer;
  90. property Items[Index: Integer] : TIdSASLListEntry read GetItem write SetItem; default;
  91. property OwnerComponent: TComponent read GetOwnerComponent;
  92. end;
  93. EIdSASLException = class(EIdException);
  94. EIdSASLNotSupported = class(EIdSASLException);
  95. EIdSASLNotReady = class(EIdSASLException);
  96. EIdSASLMechNeeded = class(EIdSASLException);
  97. implementation
  98. uses
  99. {$IFDEF HAS_UNIT_Generics_Collections}
  100. System.Generics.Collections,
  101. {$ENDIF}
  102. IdAssignedNumbers,
  103. IdCoderMIME,
  104. IdGlobalProtocols,
  105. IdReply,
  106. IdResourceStringsProtocols,
  107. SysUtils;
  108. { TIdSASLListEntry }
  109. procedure TIdSASLListEntry.Assign(Source: TPersistent);
  110. begin
  111. if Source is TIdSASLListEntry then begin
  112. SASL := TIdSASLListEntry(Source).SASL;
  113. end else begin
  114. inherited Assign(Source);
  115. end;
  116. end;
  117. function TIdSASLListEntry.GetDisplayName: String;
  118. begin
  119. if FSASL <> nil then begin
  120. Result := String(FSASL.ServiceName);
  121. end else begin
  122. Result := inherited GetDisplayName;
  123. end;
  124. end;
  125. function TIdSASLListEntry.GetOwnerComponent: TComponent;
  126. var
  127. LEntries: TIdSASLEntries;
  128. begin
  129. LEntries := SASLEntries;
  130. if Assigned(LEntries) then begin
  131. Result := LEntries.OwnerComponent;
  132. end else begin
  133. Result := nil;
  134. end;
  135. end;
  136. function TIdSASLListEntry.GetSASLEntries: TIdSASLEntries;
  137. begin
  138. if Collection is TIdSASLEntries then begin
  139. Result := TIdSASLEntries(Collection);
  140. end else begin
  141. Result := nil;
  142. end;
  143. end;
  144. procedure TIdSASLListEntry.SetSASL(AValue : TIdSASL);
  145. var
  146. LOwnerComp: TComponent;
  147. begin
  148. if FSASL <> AValue then begin
  149. LOwnerComp := OwnerComponent;
  150. if (FSASL <> nil) and (LOwnerComp <> nil) then begin
  151. FSASL.RemoveFreeNotification(LOwnerComp);
  152. end;
  153. FSASL := AValue;
  154. if (FSASL <> nil) and (LOwnerComp <> nil) then begin
  155. FSASL.FreeNotification(LOwnerComp);
  156. end;
  157. end;
  158. end;
  159. { TIdSASLEntries }
  160. // RLebeau 2/8/2013: WARNING!!! To work around a design limitation in the way
  161. // TIdIMAP4 implements SendCmd(), it cannot use TIdSASLEntries.LoginSASL() for
  162. // SASL authentication because the SASL commands sent in this unit will not end
  163. // up being IMAP-compatible! Until that can be addressed, any changes made to
  164. // PerformSASLLogin() or LoginSASL() in this unit need to be duplicated in the
  165. // IdIMAP4.pas unit for the TIdIMAP4.Login() method as well...
  166. function CheckStrFail(const AStr : String; const AOk, ACont: array of string) : Boolean;
  167. begin
  168. Result := (PosInStrArray(AStr, AOk) = -1) and
  169. (PosInStrArray(AStr, ACont) = -1);
  170. end;
  171. function PerformSASLLogin(const ACmd, AHost: string; const APort: TIdPort; const AProtocolName: String;
  172. ASASL: TIdSASL; AEncoder: TIdEncoder; ADecoder: TIdDecoder; const AOkReplies, AContinueReplies: array of string;
  173. AClient : TIdTCPConnection; ACanAttemptIR: Boolean): Boolean;
  174. var
  175. S: String;
  176. AuthStarted: Boolean;
  177. begin
  178. Result := False;
  179. AuthStarted := False;
  180. // TODO: handle ACanAttemptIR based on AProtocolName.
  181. //
  182. // SASL in SMTP and DICT supported Initial-Response from the beginning,
  183. // as should any new SASL-enabled protocol moving forward.
  184. //
  185. // SASL in IMAP did not originally support Initial-Response, but it was
  186. // added in RFC 4959 along with an explicit capability ('SASL-IR') to
  187. // indicate when Initial-Response is supported. SASL in IMAP is currently
  188. // handled by TIdIMAP4 directly, but should it be updated to use
  189. // TIdSASLEntries.LoginSASL() in the future then it will set the
  190. // ACanAttemptIR parameter accordingly.
  191. //
  192. // SASL in POP3 did not originally support Initial-Response. It was added
  193. // in RFC 2449 along with the CAPA command. If a server supports the CAPA
  194. // command then it *should* also support Initial-Response as well, however
  195. // many POP3 servers support CAPA but do not support Initial-Response
  196. // (which was formalized in RFC 5034). So, to handle that descrepency,
  197. // TIdPOP3 currently sets ACanAttemptIR to false. In the future, we could
  198. // let it set ACanAttemptIR to True instead, and then if Initial-Response
  199. // fails here for POP3 then re-attempt without Initial-Response before
  200. // exiting with a failure.
  201. // TODO: use UTF-8 when base64-encoding strings...
  202. if ACanAttemptIR then begin
  203. if ASASL.TryStartAuthenticate(AHost, APort, AProtocolName, S) then begin
  204. AClient.SendCmd(ACmd + ' ' + String(ASASL.ServiceName) + ' ' + AEncoder.Encode(S));//[334, 504]
  205. if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then begin
  206. // TODO: re-attempt without IR unconditionally? Or add a callback
  207. // to let the caller decide whether to re-attempt or not...
  208. if not TextIsSame(AProtocolName, IdGSKSSN_pop) then begin
  209. ASASL.FinishAuthenticate;
  210. Exit; // this mechanism is not supported
  211. end;
  212. end else begin
  213. AuthStarted := True;
  214. end;
  215. end;
  216. end;
  217. if not AuthStarted then begin
  218. AClient.SendCmd(ACmd + ' ' + String(ASASL.ServiceName));//[334, 504]
  219. if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then begin
  220. Exit; // this mechanism is not supported
  221. end;
  222. end;
  223. if (PosInStrArray(AClient.LastCmdResult.Code, AOkReplies) > -1) then begin
  224. if AuthStarted then begin
  225. ASASL.FinishAuthenticate;
  226. end;
  227. Result := True;
  228. Exit; // we've authenticated successfully :)
  229. end;
  230. // must be a continue reply...
  231. if not AuthStarted then begin
  232. S := ADecoder.DecodeString(TrimRight(AClient.LastCmdResult.Text.Text));
  233. S := ASASL.StartAuthenticate(S, AHost, APort, AProtocolName);
  234. AClient.SendCmd(AEncoder.Encode(S));
  235. if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then
  236. begin
  237. ASASL.FinishAuthenticate;
  238. Exit;
  239. end;
  240. end;
  241. while PosInStrArray(AClient.LastCmdResult.Code, AContinueReplies) > -1 do begin
  242. S := ADecoder.DecodeString(TrimRight(AClient.LastCmdResult.Text.Text));
  243. S := ASASL.ContinueAuthenticate(S, AHost, APort, AProtocolName);
  244. AClient.SendCmd(AEncoder.Encode(S));
  245. if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then
  246. begin
  247. ASASL.FinishAuthenticate;
  248. Exit;
  249. end;
  250. end;
  251. Result := (PosInStrArray(AClient.LastCmdResult.Code, AOkReplies) > -1);
  252. ASASL.FinishAuthenticate;
  253. end;
  254. function TIdSASLEntries.Add: TIdSASLListEntry;
  255. begin
  256. Result := TIdSASLListEntry(inherited Add);
  257. end;
  258. constructor TIdSASLEntries.Create(AOwner: TPersistent);
  259. begin
  260. inherited Create(AOwner, TIdSASLListEntry);
  261. end;
  262. procedure TIdSASLEntries.CheckIfEmpty;
  263. var
  264. I: Integer;
  265. begin
  266. for I := 0 to Count-1 do begin
  267. if Items[I].SASL <> nil then begin
  268. Exit;
  269. end;
  270. end;
  271. raise EIdSASLMechNeeded.Create(RSSASLRequired);
  272. end;
  273. function TIdSASLEntries.GetItem(Index: Integer): TIdSASLListEntry;
  274. begin
  275. Result := TIdSASLListEntry(inherited Items[Index]);
  276. end;
  277. function TIdSASLEntries.GetOwnerComponent: TComponent;
  278. var
  279. LOwner: TPersistent;
  280. begin
  281. LOwner := inherited GetOwner;
  282. if LOwner is TComponent then begin
  283. Result := TComponent(LOwner);
  284. end else begin
  285. Result := nil;
  286. end;
  287. end;
  288. function TIdSASLEntries.IndexOfComp(AItem: TIdSASL): Integer;
  289. begin
  290. for Result := 0 to Count -1 do
  291. begin
  292. if Items[Result].SASL = AItem then
  293. begin
  294. Exit;
  295. end;
  296. end;
  297. Result := -1;
  298. end;
  299. function TIdSASLEntries.Insert(Index: Integer): TIdSASLListEntry;
  300. begin
  301. Result := TIdSASLListEntry( inherited Insert(Index) );
  302. end;
  303. type
  304. {$IFDEF HAS_GENERICS_TList}
  305. TIdSASLList = TList<TIdSASL>;
  306. {$ELSE}
  307. // TODO: flesh out to match TList<TIdSASL> for non-Generics compilers
  308. TIdSASLList = TList;
  309. {$ENDIF}
  310. {$I IdDeprecatedImplBugOff.inc}
  311. procedure TIdSASLEntries.LoginSASL(const ACmd, AHost, AProtocolName: String; const AOkReplies,
  312. AContinueReplies: array of string; AClient: TIdTCPConnection;
  313. ACapaReply: TStrings; const AAuthString: String; ACanAttemptIR: Boolean);
  314. {$I IdDeprecatedImplBugOn.inc}
  315. begin
  316. LoginSASL(ACmd, AHost, 0, AProtocolName, AOkReplies, AContinueReplies, AClient, ACapaReply, AAuthString, ACanAttemptIR);
  317. end;
  318. procedure TIdSASLEntries.LoginSASL(const ACmd, AHost: String; const APort: TIdPort; const AProtocolName: String;
  319. const AOkReplies, AContinueReplies: array of string; AClient: TIdTCPConnection;
  320. ACapaReply: TStrings; const AAuthString: String; ACanAttemptIR: Boolean);
  321. var
  322. i : Integer;
  323. LE : TIdEncoderMIME;
  324. LD : TIdDecoderMIME;
  325. LSupportedSASL : TStrings;
  326. LSASLList: TIdSASLList;
  327. LSASL : TIdSASL;
  328. LError : TIdReply;
  329. function SetupErrorReply: TIdReply;
  330. begin
  331. Result := TIdReplyClass(AClient.LastCmdResult.ClassType).Create(nil);
  332. Result.Assign(AClient.LastCmdResult);
  333. end;
  334. begin
  335. // make sure the collection is not empty
  336. CheckIfEmpty;
  337. //create a list of mechanisms that both parties support
  338. LSASLList := TIdSASLList.Create;
  339. try
  340. LSupportedSASL := TStringList.Create;
  341. try
  342. ParseCapaReplyToList(ACapaReply, LSupportedSASL, AAuthString);
  343. for i := Count-1 downto 0 do begin
  344. LSASL := Items[i].SASL;
  345. if LSASL <> nil then begin
  346. if not LSASL.IsAuthProtocolAvailable(LSupportedSASL) then begin
  347. Continue;
  348. end;
  349. if LSASLList.IndexOf(LSASL) = -1 then begin
  350. LSASLList.Add(LSASL);
  351. end;
  352. end;
  353. end;
  354. finally
  355. FreeAndNil(LSupportedSASL);
  356. end;
  357. if LSASLList.Count = 0 then begin
  358. raise EIdSASLNotSupported.Create(RSSASLNotSupported);
  359. end;
  360. //now do it
  361. LE := nil;
  362. try
  363. LD := nil;
  364. try
  365. LError := nil;
  366. try
  367. for i := 0 to LSASLList.Count-1 do begin
  368. LSASL := {$IFDEF HAS_GENERICS_TList}LSASLList.Items[i]{$ELSE}TIdSASL(LSASLList.Items[i]){$ENDIF};
  369. if not LSASL.IsReadyToStart then begin
  370. Continue;
  371. end;
  372. if not Assigned(LE) then begin
  373. LE := TIdEncoderMIME.Create(nil);
  374. end;
  375. if not Assigned(LD) then begin
  376. LD := TIdDecoderMIME.Create(nil);
  377. end;
  378. if PerformSASLLogin(ACmd, AHost, APort, AProtocolName, LSASL, LE, LD, AOkReplies, AContinueReplies, AClient, ACanAttemptIR) then begin
  379. Exit;
  380. end;
  381. if not Assigned(LError) then begin
  382. LError := SetupErrorReply;
  383. end;
  384. end;
  385. if Assigned(LError) then begin
  386. LError.RaiseReplyError;
  387. end else begin
  388. raise EIdSASLNotReady.Create(RSSASLNotReady);
  389. end;
  390. finally
  391. FreeAndNil(LError);
  392. end;
  393. finally
  394. FreeAndNil(LD);
  395. end;
  396. finally
  397. FreeAndNil(LE);
  398. end;
  399. finally
  400. FreeAndNil(LSASLList);
  401. end;
  402. end;
  403. {$I IdDeprecatedImplBugOff.inc}
  404. procedure TIdSASLEntries.LoginSASL(const ACmd, AHost, AProtocolName, AServiceName: String;
  405. const AOkReplies, AContinueReplies: array of string; AClient: TIdTCPConnection;
  406. ACapaReply: TStrings; const AAuthString: String; ACanAttemptIR: Boolean);
  407. {$I IdDeprecatedImplBugOn.inc}
  408. begin
  409. LoginSASL(ACmd, AHost, 0, AProtocolName, AServiceName, AOkReplies, AContinueReplies, AClient, ACapaReply, AAuthString, ACanAttemptIR);
  410. end;
  411. procedure TIdSASLEntries.LoginSASL(const ACmd, AHost: String; const APort: TIdPort;
  412. const AProtocolName, AServiceName: String;
  413. const AOkReplies, AContinueReplies: array of string; AClient: TIdTCPConnection;
  414. ACapaReply: TStrings; const AAuthString: String; ACanAttemptIR: Boolean);
  415. var
  416. LE : TIdEncoderMIME;
  417. LD : TIdDecoderMIME;
  418. LSupportedSASL : TStrings;
  419. LSASL : TIdSASL;
  420. begin
  421. LSASL := nil;
  422. // make sure the collection is not empty
  423. CheckIfEmpty;
  424. //determine if both parties support the same mechanism
  425. LSupportedSASL := TStringList.Create;
  426. try
  427. ParseCapaReplyToList(ACapaReply, LSupportedSASL, AAuthString);
  428. if LSupportedSASL.IndexOf(AServiceName) <> -1 then begin
  429. LSASL := FindSASL(AServiceName);
  430. end;
  431. finally
  432. FreeAndNil(LSupportedSASL);
  433. end;
  434. if LSASL = nil then begin
  435. raise EIdSASLNotSupported.Create(RSSASLNotSupported);
  436. end;
  437. if not LSASL.IsReadyToStart then begin
  438. raise EIdSASLNotReady.Create(RSSASLNotReady);
  439. end;
  440. //now do it
  441. LE := TIdEncoderMIME.Create(nil);
  442. try
  443. LD := TIdDecoderMIME.Create(nil);
  444. try
  445. if not PerformSASLLogin(ACmd, AHost, APort, AProtocolName, LSASL, LE, LD, AOkReplies, AContinueReplies, AClient, ACanAttemptIR) then begin
  446. AClient.RaiseExceptionForLastCmdResult;
  447. end;
  448. finally
  449. FreeAndNil(LD);
  450. end;
  451. finally
  452. FreeAndNil(LE);
  453. end;
  454. end;
  455. {$I IdDeprecatedImplBugOff.inc}
  456. function TIdSASLEntries.ParseCapaReply(ACapaReply: TStrings; const AAuthString: String): TStrings;
  457. {$I IdDeprecatedImplBugOn.inc}
  458. begin
  459. Result := TStringList.Create;
  460. try
  461. ParseCapaReplyToList(ACapaReply, Result, AAuthString);
  462. except
  463. FreeAndNil(Result);
  464. raise;
  465. end;
  466. end;
  467. procedure TIdSASLEntries.ParseCapaReplyToList(ACapaReply, ADestList: TStrings;
  468. const AAuthString: String = 'AUTH'); {do not localize}
  469. const
  470. VALIDDELIMS: String = ' ='; {Do not Localize}
  471. var
  472. i: Integer;
  473. s: string;
  474. LEntry : String;
  475. begin
  476. if ACapaReply = nil then begin
  477. Exit;
  478. end;
  479. ADestList.BeginUpdate;
  480. try
  481. for i := 0 to ACapaReply.Count - 1 do
  482. begin
  483. s := ACapaReply[i];
  484. if TextStartsWith(s, AAuthString) and CharIsInSet(s, Length(AAuthString)+1, VALIDDELIMS) then
  485. begin
  486. s := UpperCase(Copy(s, Length(AAuthString)+1, MaxInt));
  487. s := ReplaceAll(s, '=', ' '); {Do not Localize}
  488. while Length(s) > 0 do
  489. begin
  490. LEntry := Fetch(s, ' '); {Do not Localize}
  491. if LEntry <> '' then
  492. begin
  493. if ADestList.IndexOf(LEntry) = -1 then begin
  494. ADestList.Add(LEntry);
  495. end;
  496. end;
  497. end;
  498. end;
  499. end;
  500. finally
  501. ADestList.EndUpdate;
  502. end;
  503. end;
  504. function TIdSASLEntries.FindSASL(const AServiceName: String): TIdSASL;
  505. var
  506. i: Integer;
  507. LEntry: TIdSASLListEntry;
  508. begin
  509. Result := nil;
  510. For i := 0 to Count-1 do begin
  511. LEntry := Items[i];
  512. if LEntry.SASL <> nil then begin
  513. if TextIsSame(String(LEntry.SASL.ServiceName), AServiceName) then begin
  514. Result := LEntry.SASL;
  515. Exit;
  516. end;
  517. end;
  518. end;
  519. end;
  520. procedure TIdSASLEntries.RemoveByComp(AComponent: TComponent);
  521. var
  522. i : Integer;
  523. begin
  524. for i := Count-1 downto 0 do
  525. begin
  526. if Items[i].SASL = AComponent then begin
  527. Delete(i);
  528. end;
  529. end;
  530. end;
  531. procedure TIdSASLEntries.SetItem(Index: Integer; const Value: TIdSASLListEntry);
  532. begin
  533. inherited SetItem(Index, Value);
  534. end;
  535. end.