IdEMailAddress.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  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.13 10/26/2004 9:09:36 PM JPMugaas
  18. Updated references.
  19. Rev 1.12 24/10/2004 21:25:18 ANeillans
  20. Modifications to allow Username and Domain parts to be set.
  21. Rev 1.11 24.08.2004 17:29:30 Andreas Hausladen
  22. Fixed GetEMailAddresses
  23. Lots of simple but effective optimizations
  24. Rev 1.10 09/08/2004 08:17:08 ANeillans
  25. Rename username property to user
  26. Rev 1.9 08/08/2004 20:58:02 ANeillans
  27. Added support for Username extraction.
  28. Rev 1.8 23/04/2004 20:34:36 CCostelloe
  29. Clarified a question in the code as to why a code path ended there
  30. Rev 1.7 3/6/2004 5:45:00 PM JPMugaas
  31. Fixed problem obtaining the Text property for an E-Mail address with
  32. no domain.
  33. Rev 1.6 2004.02.03 5:45:08 PM czhower
  34. Name changes
  35. Rev 1.5 24/01/2004 19:12:10 CCostelloe
  36. Cleaned up warnings
  37. Rev 1.4 10/12/2003 7:51:50 PM BGooijen
  38. Fixed Range Check Error
  39. Rev 1.3 10/8/2003 9:50:24 PM GGrieve
  40. use IdDelete
  41. Rev 1.2 6/10/2003 5:48:50 PM SGrobety
  42. DotNet updates
  43. Rev 1.1 5/18/2003 02:30:36 PM JPMugaas
  44. Added some backdoors for the TIdDirectSMTP processing.
  45. Rev 1.0 11/14/2002 02:19:44 PM JPMugaas
  46. 2001-Aug-30 - Jim Gunkel
  47. Fixed bugs that would occur with group names containing spaces
  48. (box test 19) and content being located after the email
  49. address (box test 33)
  50. 2001-Jul-11 - Allen O'Neill
  51. Added hack to not allow recipient entries being added that are blank
  52. 2001-Jul-11 - Allen O'Neill
  53. Added hack to accomodate a PERIOD (#46) in an email address -
  54. this whole area needs to be looked at.
  55. 2001-Feb-03 - Peter Mee
  56. Overhauled TIdEMailAddressItem.GetText to support non-standard textual
  57. elements.
  58. 2001-Jan-29 - Peter Mee
  59. Overhauled TIdEMailAddressList.SetEMailAddresses to support comments
  60. and escaped characters and to ignore groups.
  61. 2001-Jan-28 - Peter Mee
  62. Overhauled TIdEMailAddressItem.SetText to support comments and escaped
  63. characters.
  64. 2000-Jun-10 - J. Peter Mugaas
  65. started this unit to facilitate some Indy work including the
  66. TIdEMailAddressItem and TIdEMailAddressList classes
  67. The GetText and SetText were originally the ToArpa and FromArpa
  68. functions in the TIdMessage component
  69. }
  70. unit IdEMailAddress;
  71. {
  72. Developer(s):
  73. J. Peter Mugaas
  74. Contributor(s):
  75. Ciaran Costelloe
  76. Bas Gooijen
  77. Grahame Grieve
  78. Stephane Grobety
  79. Jim Gunkel
  80. Andreas Hausladen
  81. Peter Mee
  82. Andy Neillans
  83. Allen O'Neill
  84. }
  85. interface
  86. {$i IdCompilerDefines.inc}
  87. uses
  88. Classes,
  89. IdException;
  90. type
  91. EIdEmailParseError = class(EIdException);
  92. { ToDo: look into alterations required for TIdEMailAddressItem.GetText }
  93. TIdEMailAddressItem = class(TCollectionItem)
  94. protected
  95. FAddress: string;
  96. FName: string;
  97. function GetText: string;
  98. procedure SetText(AText: string);
  99. function ConvertAddress: string;
  100. function GetDomain: string;
  101. procedure SetDomain(const ADomain: String);
  102. function GetUsername: string;
  103. procedure SetUsername(const AUsername: String);
  104. public
  105. procedure Assign(Source: TPersistent); override;
  106. constructor Create; reintroduce; overload;
  107. constructor Create(ACollection: TCollection); overload; override;
  108. constructor Create(const AText: string); reintroduce; overload;
  109. published
  110. {This is the E-Mail address itself }
  111. property Address: string read FAddress write FAddress;
  112. { This is the person's name }
  113. property Name: string read FName write FName;
  114. { This is the combined person's name and E-Mail address }
  115. property Text: string read GetText write SetText;
  116. {Extracted domain for some types of E-Mail processing}
  117. property Domain: string read GetDomain write SetDomain;
  118. property User: string read GetUsername write SetUsername;
  119. end;
  120. TIdEMailAddressList = class (TOwnedCollection)
  121. protected
  122. function GetItem(Index: Integer): TIdEMailAddressItem;
  123. procedure SetItem(Index: Integer; const Value: TIdEMailAddressItem);
  124. function GetEMailAddresses: string;
  125. procedure SetEMailAddresses(AList: string);
  126. public
  127. constructor Create(AOwner: TPersistent); reintroduce;
  128. { List of formated addresses including the names from the collection }
  129. procedure FillTStrings(AStrings: TStrings);
  130. function Add: TIdEMailAddressItem; reintroduce;
  131. procedure AddItems(AList: TIdEMailAddressList);
  132. { get all of the domains in the list so we can process individually }
  133. procedure GetDomains(AStrings: TStrings);
  134. { Sort by domains for making it easier to process E-Mails directly }
  135. procedure SortByDomain;
  136. { Gets all E-Mail addresses for a particular domain so we can
  137. send to recipients at one domain with only one connection }
  138. procedure AddressesByDomain(AList: TIdEMailAddressList; const ADomain: string);
  139. property Items[Index: Integer]: TIdEMailAddressItem read GetItem write SetItem; default;
  140. { Comma-separated list of formated addresses including the names
  141. from the collection }
  142. property EMailAddresses: string read GetEMailAddresses write SetEMailAddresses;
  143. end;
  144. implementation
  145. uses
  146. IdGlobal,
  147. IdGlobalProtocols,
  148. SysUtils;
  149. const
  150. // ATEXT without the double quote and space characters
  151. IETF_ATEXT: string = 'abcdefghijklmnopqrstuvwxyz' + {do not localize}
  152. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + {do not localize}
  153. '1234567890!#$%&''*+-/=?_`{}|~'; {do not localize}
  154. // ATEXT without the double quote character
  155. IETF_ATEXT_SPACE: string = 'abcdefghijklmnopqrstuvwxyz' + {do not localize}
  156. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + {do not localize}
  157. '1234567890!#$%&''*+-/=?_`{}|~ '; {do not localize}
  158. IETF_QUOTABLE: string = '\"'; {do not localize}
  159. { TIdEMailAddressItem }
  160. constructor TIdEMailAddressItem.Create;
  161. begin
  162. inherited Create(nil);
  163. end;
  164. constructor TIdEMailAddressItem.Create(ACollection: TCollection);
  165. begin
  166. inherited Create(ACollection);
  167. end;
  168. constructor TIdEMailAddressItem.Create(const AText: string);
  169. begin
  170. inherited Create(nil);
  171. Text := AText;
  172. end;
  173. procedure TIdEMailAddressItem.Assign(Source: TPersistent);
  174. var
  175. LAddr : TIdEMailAddressItem;
  176. begin
  177. if Source is TIdEMailAddressItem then begin
  178. LAddr := TIdEMailAddressItem(Source);
  179. Address := LAddr.Address;
  180. Name := LAddr.Name;
  181. end else begin
  182. inherited Assign(Source);
  183. end;
  184. end;
  185. function TIdEMailAddressItem.ConvertAddress: string;
  186. var
  187. i: Integer;
  188. domainPart, tempAddress, localPart: string;
  189. begin
  190. if FAddress = '' then
  191. begin
  192. if FName <> '' then
  193. begin
  194. Result := '<>'; {Do not Localize}
  195. end else
  196. begin
  197. Result := ''; {Do not Localize}
  198. end;
  199. Exit;
  200. end;
  201. // First work backwards to the @ sign.
  202. tempAddress := FAddress;
  203. domainPart := '';
  204. for i := Length(FAddress) downto 1 do
  205. begin
  206. if FAddress[i] = '@' then {do not localize}
  207. begin
  208. domainPart := Copy(FAddress, i, MaxInt);
  209. tempAddress := Copy(FAddress, 1, i - 1);
  210. Break;
  211. end;
  212. end;
  213. i := FindFirstNotOf(IETF_ATEXT, tempAddress);
  214. // hack to accomodate periods in emailaddress
  215. if (i = 0) or CharEquals(tempAddress, i, #46) then
  216. begin
  217. if FName <> '' then begin
  218. Result := '<' + tempAddress + domainPart + '>'; {do not localize}
  219. end else begin
  220. Result := tempAddress + domainPart;
  221. end;
  222. end else
  223. begin
  224. localPart := '"'; {do not localize}
  225. while i > 0 do
  226. begin
  227. localPart := localPart + Copy(tempAddress, 1, i - 1);
  228. if IndyPos(tempAddress[i], IETF_QUOTABLE) > 0 then
  229. begin
  230. localPart := localPart + '\'; {do not localize}
  231. end;
  232. localPart := localPart + tempAddress[i];
  233. IdDelete(tempAddress, 1, i);
  234. i := FindFirstNotOf(IETF_ATEXT, tempAddress);
  235. end;
  236. Result := '<' + localPart + tempAddress + '"' + domainPart + '>'; {do not localize}
  237. end;
  238. end;
  239. function TIdEMailAddressItem.GetDomain: string;
  240. var
  241. i: Integer;
  242. begin
  243. Result := '';
  244. // TODO: use RPos() or LastDelimiter() instead of a manual loop...
  245. for i := Length(FAddress) downto 1 do
  246. begin
  247. if FAddress[i] = '@' then {do not localize}
  248. begin
  249. Result := Copy(FAddress, i + 1, MaxInt);
  250. Break;
  251. end;
  252. end;
  253. end;
  254. procedure TIdEMailAddressItem.SetDomain(const ADomain: String);
  255. var
  256. S : String;
  257. lPos: Integer;
  258. begin
  259. S := FAddress;
  260. // keep existing user info in the address... use new domain info
  261. lPos := IndyPos('@', S); {do not localize}
  262. if lPos > 0 then begin
  263. IdDelete(S, lPos, Length(S));
  264. end;
  265. FAddress := S + '@' + ADomain; {do not localize}
  266. end;
  267. function TIdEMailAddressItem.GetUsername: string;
  268. var
  269. i: Integer;
  270. begin
  271. Result := '';
  272. for i := Length(FAddress) downto 1 do
  273. begin
  274. if FAddress[i] = '@' then {do not localize}
  275. begin
  276. Result := Copy(FAddress, 1, i - 1);
  277. Break;
  278. end;
  279. end;
  280. end;
  281. procedure TIdEMailAddressItem.SetUsername(const AUsername: String);
  282. var
  283. S : String;
  284. lPos: Integer;
  285. begin
  286. S := FAddress;
  287. // discard old user info... keep existing domain in the address
  288. lPos := IndyPos('@', S);
  289. if lPos > 0 then begin
  290. IdDelete(S, 1, lPos); {do not localize}
  291. end;
  292. FAddress := AUsername + '@' + S;
  293. end;
  294. function TIdEMailAddressItem.GetText: string;
  295. var
  296. i: Integer;
  297. tempName, resName: string;
  298. begin
  299. if (FName <> '') and (not TextIsSame(FAddress, FName)) then
  300. begin
  301. i := FindFirstNotOf(IETF_ATEXT_SPACE, FName);
  302. if i > 0 then
  303. begin
  304. // Need to quote the FName.
  305. resName := '"' + Copy(FName, 1, i - 1); {do not localize}
  306. if IndyPos(FName[i], IETF_QUOTABLE) > 0 then
  307. begin
  308. resName := resName + '\'; {do not localize}
  309. end;
  310. resName := resName + FName[i];
  311. tempName := Copy(FName, i + 1, MaxInt);
  312. while tempName <> '' do
  313. begin
  314. i := FindFirstNotOf(IETF_ATEXT_SPACE, tempName);
  315. if i = 0 then
  316. begin
  317. Result := resName + tempName + '" ' + ConvertAddress; {do not localize}
  318. Exit;
  319. end;
  320. resName := resName + Copy(tempName, 1, i - 1);
  321. if IndyPos(tempName[i], IETF_QUOTABLE) > 0 then
  322. begin
  323. resName := resName + '\'; {do not localize}
  324. end;
  325. resName := resName + tempName[i];
  326. IdDelete(tempName, 1, i);
  327. end;
  328. Result := resName + '" ' + ConvertAddress; {do not localize}
  329. end else
  330. begin
  331. Result := FName + ' ' + ConvertAddress; {do not localize}
  332. end;
  333. end else
  334. begin
  335. Result := ConvertAddress;
  336. end;
  337. end;
  338. procedure TIdEMailAddressItem.SetText(AText: string);
  339. var
  340. nFirst,
  341. nBracketCount: Integer;
  342. bInAddress,
  343. bAddressInLT,
  344. bAfterAt,
  345. bInQuote : Boolean;
  346. begin
  347. FAddress := '';
  348. FName := '';
  349. AText := Trim(AText);
  350. if AText = '' then begin
  351. Exit;
  352. end;
  353. // Find the first known character type.
  354. if Pos('<', AText) > 0 then begin
  355. nFirst := FindFirstOf('("< ' + TAB, AText) {Do not Localize}
  356. end else begin
  357. nFirst := FindFirstOf('(" @' + TAB, AText); {Do not Localize}
  358. end;
  359. if nFirst <> 0 then
  360. begin
  361. nBracketCount := 0;
  362. bInAddress := False;
  363. bAddressInLT := False;
  364. bInQuote := False;
  365. bAfterAt := False;
  366. repeat
  367. case AText[nFirst] of
  368. ' ', TAB : {do not localize}
  369. begin
  370. if nFirst = 1 then
  371. begin
  372. IdDelete(AText, 1, 1);
  373. end else
  374. begin
  375. // Only valid if in a name not contained in quotes - keep the space.
  376. if bAfterAt then begin
  377. FAddress := FAddress + Trim(Copy(AText, 1, nFirst - 1));
  378. end else begin
  379. FName := FName + Copy(AText, 1, nFirst);
  380. end;
  381. IdDelete(AText, 1, nFirst);
  382. end;
  383. end;
  384. '(' : {do not localize}
  385. begin
  386. Inc(nBracketCount);
  387. if nFirst > 1 then
  388. begin
  389. // There's at least one character to the name
  390. if bInAddress then
  391. begin
  392. FAddress := FAddress + Trim(Copy(AText, 1, nFirst - 1));
  393. end
  394. else if nBracketCount = 1 then
  395. begin
  396. FName := FName + Copy(AText, 1, nFirst - 1);
  397. end;
  398. IdDelete(AText, 1, nFirst);
  399. end else
  400. begin
  401. IdDelete(AText, 1, 1);
  402. end;
  403. end;
  404. ')' : {do not localize}
  405. begin
  406. Dec(nBracketCount);
  407. IdDelete(AText, 1, nFirst);
  408. end;
  409. '"' : {do not localize}
  410. begin
  411. if bInQuote then
  412. begin
  413. if bAddressInLT then
  414. begin
  415. FAddress := FAddress + Trim(Copy(AText, 1, nFirst - 1));
  416. end else begin
  417. FName := FName + Trim(Copy(AText, 1, nFirst - 1));
  418. end;
  419. IdDelete(AText, 1, nFirst);
  420. bInQuote := False;
  421. end else
  422. begin
  423. bInQuote := True;
  424. IdDelete(AText, 1, 1);
  425. end;
  426. end;
  427. '<' : {do not localize}
  428. begin
  429. if nFirst > 1 then
  430. begin
  431. FName := FName + Copy(AText, 1, nFirst - 1);
  432. end;
  433. FName := TrimAllOf(' ' + TAB, Trim(FName)); {do not localize}
  434. bAddressInLT := True;
  435. bInAddress := True;
  436. IdDelete(AText, 1, nFirst);
  437. end;
  438. '>' : {do not localize}
  439. begin
  440. // Only searched for if the address starts with '<'
  441. bInAddress := False;
  442. bAfterAt := False;
  443. FAddress := FAddress + TrimAllOf(' ' + TAB, {do not localize}
  444. Trim(Copy(AText, 1, nFirst - 1)));
  445. IdDelete(AText, 1, nFirst);
  446. end;
  447. '@' : {do not localize}
  448. begin
  449. bAfterAt := True;
  450. if bInAddress then
  451. begin
  452. FAddress := FAddress + Copy(AText, 1, nFirst);
  453. IdDelete(AText, 1, nFirst);
  454. end else
  455. begin
  456. if bAddressInLT then
  457. begin
  458. {
  459. Strange use. For now raise an exception until a real-world
  460. example can be found.
  461. Basically, it's formatted as follows:
  462. <[email protected]> some-text @ some-text
  463. or:
  464. some-text <[email protected]> some-text @ some-text
  465. where some text may be blank. Note you used to arrive here
  466. if the From header in an email included more than one address
  467. (which was subsequently changed) because our code did not
  468. parse the From header for multiple addresses. That may have
  469. been the reason for this code.
  470. }
  471. //raise EIdEmailParseError.Create(RSEMailSymbolOutsideAddress);
  472. FName := FName + AText;
  473. Exit;
  474. end;
  475. {
  476. at this point, we're either supporting an e-mail address on
  477. it's own, or the old-style valid format:
  478. "Name" [email protected]
  479. }
  480. bInAddress := True;
  481. FAddress := FAddress + Copy(AText, 1, nFirst);
  482. IdDelete(AText, 1, nFirst);
  483. end;
  484. end;
  485. '.' : {do not localize}
  486. begin
  487. // Must now be a part of the domain part of the address.
  488. if bAddressInLT then
  489. begin
  490. // Whitespace is possible around the parts of the domain.
  491. FAddress := FAddress +
  492. TrimAllOf(' ' + TAB, Trim(Copy(AText, 1, nFirst - 1))) + '.'; {do not localize}
  493. AText := TrimLeft(Copy(AText, nFirst + 1, MaxInt));
  494. end else
  495. begin
  496. // No whitespace is allowed if no wrapping <> characters.
  497. FAddress := FAddress + Copy(AText, 1, nFirst);
  498. IdDelete(AText, 1, nFirst);
  499. end;
  500. end;
  501. '\' : {do not localize}
  502. begin
  503. {
  504. This will only be discovered in a bracketed or quoted section.
  505. It's an escape character indicating the next character is a literal.
  506. }
  507. if bInQuote then
  508. begin
  509. // Need to retain the second character
  510. if bInAddress then
  511. begin
  512. FAddress := FAddress + Copy(AText, 1, nFirst - 1);
  513. FAddress := FAddress + AText[nFirst + 1];
  514. end else
  515. begin
  516. FName := FName + Copy(AText, 1, nFirst - 1);
  517. FName := FName + AText[nFirst + 1];
  518. end;
  519. end;
  520. IdDelete(AText, 1, nFirst + 1);
  521. end;
  522. end;
  523. {
  524. Check for bracketted sections first:
  525. ("<>" <> "" <"">) - all is ignored
  526. }
  527. if nBracketCount > 0 then
  528. begin
  529. {
  530. Inside a bracket, only three characters are special.
  531. '(' Opens a nested bracket: (One (Two (Three )))
  532. ')' Closes a bracket
  533. '\' Escape character: (One \) \( \\ (Two \) ))
  534. }
  535. nFirst := FindFirstOf('()\', AText); {do not localize}
  536. // Check if in quote before address: <"My Name"@domain.example> is valid
  537. end else if bInQuote then
  538. begin
  539. // Inside quotes, only the end quote and escape character are special.
  540. // previously FindFirst. This fixes a bug in From: like: "This is "my" name" <[email protected]> delivered from DecodeHeader
  541. nFirst := LastDelimiter('"\', AText); {do not localize}
  542. // Check if after the @ of the address: domain.example>
  543. end else if bAfterAt then
  544. begin
  545. if bAddressInLT then
  546. begin
  547. {
  548. If the address is enclosed, then only the '(', '.' & '>'
  549. need be looked for, trimming all content when found:
  550. domain . example >
  551. }
  552. nFirst := FindFirstOf('.>(', AText); {do not localize}
  553. end else begin
  554. nFirst := FindFirstOf('.( ', AText); {Do not Localize}
  555. end;
  556. // Check if in address: <[email protected]>
  557. end else if bInAddress then
  558. begin
  559. nFirst := FindFirstOf('"(@>', AText); {do not localize}
  560. // Not in anything - check for opening character
  561. end else
  562. begin
  563. // Outside brackets
  564. nFirst := FindFirstOf('("< @' + TAB, AText); {do not localize}
  565. end;
  566. until nFirst = 0;
  567. if bInAddress and (not bAddressInLT) then
  568. begin
  569. FAddress := FAddress + TrimAllOf(' ' + TAB, Trim(AText)); {do not localize}
  570. end;
  571. end else
  572. begin
  573. // No special characters, so assume a simple address
  574. FAddress := AText;
  575. end;
  576. end;
  577. { TIdEMailAddressList }
  578. constructor TIdEMailAddressList.Create(AOwner: TPersistent);
  579. begin
  580. inherited Create(AOwner, TIdEMailAddressItem);
  581. end;
  582. function TIdEMailAddressList.Add: TIdEMailAddressItem;
  583. begin
  584. Result := TIdEMailAddressItem(inherited Add);
  585. end;
  586. procedure TIdEMailAddressList.AddItems(AList: TIdEMailAddressList);
  587. var
  588. I: Integer;
  589. begin
  590. if AList <> nil then begin
  591. for I := 0 to AList.Count-1 do begin
  592. Add.Assign(AList[I]);
  593. end;
  594. end;
  595. end;
  596. procedure TIdEMailAddressList.FillTStrings(AStrings: TStrings);
  597. var
  598. idx: Integer;
  599. begin
  600. for idx := 0 to Count - 1 do
  601. begin
  602. AStrings.Add(GetItem(idx).Text);
  603. end;
  604. end;
  605. function TIdEMailAddressList.GetItem(Index: Integer): TIdEMailAddressItem;
  606. begin
  607. Result := TIdEMailAddressItem(inherited Items[Index]);
  608. end;
  609. function TIdEMailAddressList.GetEMailAddresses: string;
  610. var
  611. idx: Integer;
  612. begin
  613. Result := ''; {Do not Localize}
  614. for idx := 0 to Count - 1 do
  615. begin
  616. if Result = '' then
  617. Result := GetItem(idx).Text
  618. else
  619. Result := Result + ', ' + GetItem(idx).Text; {do not localize}
  620. end;
  621. end;
  622. procedure TIdEMailAddressList.SetItem(Index: Integer;
  623. const Value: TIdEMailAddressItem);
  624. begin
  625. inherited SetItem(Index, Value);
  626. end;
  627. procedure TIdEMailAddressList.SetEMailAddresses(AList: string);
  628. var
  629. EMail : TIdEMailAddressItem;
  630. iStart: Integer;
  631. sTemp: string;
  632. nInBracket: Integer;
  633. bInQuote : Boolean;
  634. begin
  635. Clear;
  636. if Trim(AList) = '' then begin {Do not Localize}
  637. Exit;
  638. end;
  639. iStart := FindFirstOf(':;(", ' + TAB, AList); {do not localize}
  640. if iStart = 0 then
  641. begin
  642. EMail := Add;
  643. EMail.Text := TrimLeft(AList);
  644. end else
  645. begin
  646. sTemp := ''; {do not localize}
  647. nInBracket := 0;
  648. bInQuote := False;
  649. repeat
  650. case AList[iStart] of
  651. ' ', TAB: {do not localize}
  652. begin
  653. if iStart = 1 then begin
  654. sTemp := sTemp + AList[iStart];
  655. IdDelete(AList, 1, 1);
  656. end else begin
  657. sTemp := sTemp + Copy(AList, 1, iStart);
  658. IdDelete(AList, 1, iStart);
  659. end;
  660. end;
  661. ':' : {do not localize}
  662. begin
  663. // The start of a group - ignore the lot.
  664. IdDelete(AList, 1, iStart);
  665. sTemp := '';
  666. end;
  667. ';' : {do not localize}
  668. begin
  669. {
  670. End of a group. If we have something (groups can be empty),
  671. then process it.
  672. }
  673. sTemp := sTemp + Copy(AList, 1, iStart - 1);
  674. if Trim(sTemp) <> '' then
  675. begin
  676. EMail := Add;
  677. EMail.Text := TrimLeft(sTemp);
  678. sTemp := ''; {do not localize}
  679. end;
  680. // Now simply remove the end of the group.
  681. IdDelete(AList, 1, iStart);
  682. end;
  683. '(': {do not localize}
  684. begin
  685. Inc(nInBracket);
  686. sTemp := sTemp + Copy(AList, 1, iStart);
  687. IdDelete(AList, 1, iStart);
  688. end;
  689. ')': {do not localize}
  690. begin
  691. Dec(nInBracket);
  692. sTemp := sTemp + Copy(AList, 1, iStart);
  693. IdDelete(AList, 1, iStart);
  694. end;
  695. '"': {do not localize}
  696. begin
  697. sTemp := sTemp + Copy(AList, 1, iStart);
  698. IdDelete(AList, 1, iStart);
  699. bInQuote := not bInQuote;
  700. end;
  701. ',': {do not localize}
  702. begin
  703. sTemp := sTemp + Copy(AList, 1, iStart - 1);
  704. EMail := Add;
  705. EMail.Text := sTemp;
  706. // added - Allen .. saves blank entries being added
  707. sTemp := Trim(Email.Text);
  708. if (sTemp = '') or (sTemp = '<>') then {do not localize}
  709. begin
  710. IdDisposeAndNil(Email);
  711. end;
  712. sTemp := '';
  713. IdDelete(AList, 1, iStart);
  714. end;
  715. '\': {do not localize}
  716. begin
  717. // Escape character - simply copy this char and the next to the buffer.
  718. sTemp := sTemp + Copy(AList, 1, iStart + 1);
  719. IdDelete(AList, 1, iStart + 1);
  720. end;
  721. end;
  722. if nInBracket > 0 then begin
  723. iStart := FindFirstOf('(\)', AList); {Do not Localize}
  724. end else if bInQuote then begin
  725. iStart := FindFirstOf('"\', AList); {Do not Localize}
  726. end else begin
  727. iStart := FindFirstOf(':;(", ' + TAB, AList); {Do not Localize}
  728. end;
  729. until iStart = 0;
  730. // Clean up the content in sTemp
  731. if (Trim(sTemp) <> '') or (Trim(AList) <> '') then
  732. begin
  733. sTemp := sTemp + AList;
  734. EMail := Add;
  735. EMail.Text := TrimLeft(sTemp);
  736. // added - Allen .. saves blank entries being added
  737. sTemp := Trim(Email.Text);
  738. if (sTemp = '') or (sTemp = '<>') then {do not localize}
  739. begin
  740. IdDisposeAndNil(Email);
  741. end;
  742. end;
  743. end;
  744. end;
  745. procedure TIdEMailAddressList.SortByDomain;
  746. var
  747. i, j: Integer;
  748. LTemp: string;
  749. begin
  750. for i := Count-1 downto 0 do
  751. begin
  752. for j := 0 to Count-2 do
  753. begin
  754. if IndyCompareStr(Items[J].Domain, Items[J + 1].Domain) > 0 then
  755. begin
  756. LTemp := Items[j].Text;
  757. Items[j].Text := Items[j+1].Text;
  758. Items[j+1].Text := LTemp;
  759. end;
  760. end;
  761. end;
  762. end;
  763. procedure TIdEMailAddressList.GetDomains(AStrings: TStrings);
  764. var
  765. i: Integer;
  766. LCurDom: string;
  767. begin
  768. if Assigned(AStrings) then
  769. begin
  770. AStrings.BeginUpdate;
  771. try
  772. AStrings.Clear;
  773. for i := 0 to Count-1 do
  774. begin
  775. LCurDom := LowerCase(Items[i].Domain);
  776. if AStrings.IndexOf(LCurDom) = -1 then begin
  777. AStrings.Add(LCurDom);
  778. end;
  779. end;
  780. finally
  781. AStrings.EndUpdate;
  782. end;
  783. end;
  784. end;
  785. procedure TIdEMailAddressList.AddressesByDomain(AList: TIdEMailAddressList;
  786. const ADomain: string);
  787. var
  788. i: Integer;
  789. LEnt : TIdEMailAddressItem;
  790. begin
  791. AList.Clear;
  792. for i := 0 to Count-1 do
  793. begin
  794. if TextIsSame(Items[i].Domain, ADomain) then
  795. begin
  796. LEnt := AList.Add;
  797. LEnt.Text := Items[i].Text;
  798. end;
  799. end;
  800. end;
  801. end.