mimemess.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 002.006.001 |
  3. |==============================================================================|
  4. | Content: MIME message object |
  5. |==============================================================================|
  6. | Copyright (c)1999-2021, Lukas Gebauer |
  7. | All rights reserved. |
  8. | |
  9. | Redistribution and use in source and binary forms, with or without |
  10. | modification, are permitted provided that the following conditions are met: |
  11. | |
  12. | Redistributions of source code must retain the above copyright notice, this |
  13. | list of conditions and the following disclaimer. |
  14. | |
  15. | Redistributions in binary form must reproduce the above copyright notice, |
  16. | this list of conditions and the following disclaimer in the documentation |
  17. | and/or other materials provided with the distribution. |
  18. | |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may |
  20. | be used to endorse or promote products derived from this software without |
  21. | specific prior written permission. |
  22. | |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
  33. | DAMAGE. |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c)2000-2021. |
  37. | Portions created by Petr Fejfar are Copyright (c)2011-2012. |
  38. | All Rights Reserved. |
  39. |==============================================================================|
  40. | Contributor(s): |
  41. |==============================================================================|
  42. | History: see HISTORY.HTM From distribution package |
  43. | (Found at URL: http://www.ararat.cz/synapse/) |
  44. |==============================================================================}
  45. {:@abstract(MIME message handling)
  46. Classes for easy handling with e-mail message.
  47. }
  48. {$IFDEF FPC}
  49. {$MODE DELPHI}
  50. {$ENDIF}
  51. {$H+}
  52. {$M+}
  53. unit mimemess;
  54. interface
  55. uses
  56. Classes, SysUtils,
  57. mimepart, synachar, synautil, mimeinln;
  58. type
  59. {:Possible values for message priority}
  60. TMessPriority = (MP_unknown, MP_low, MP_normal, MP_high);
  61. {:@abstract(Object for basic e-mail header fields.)}
  62. TMessHeader = class(TObject)
  63. private
  64. FFrom: string;
  65. FToList: TStringList;
  66. FCCList: TStringList;
  67. FSubject: string;
  68. FOrganization: string;
  69. FCustomHeaders: TStringList;
  70. FDate: TDateTime;
  71. FXMailer: string;
  72. FCharsetCode: TMimeChar;
  73. FReplyTo: string;
  74. FMessageID: string;
  75. FPriority: TMessPriority;
  76. Fpri: TMessPriority;
  77. Fxpri: TMessPriority;
  78. Fxmspri: TMessPriority;
  79. protected
  80. function ParsePriority(value: string): TMessPriority;
  81. function DecodeHeader(value: string): boolean; virtual;
  82. public
  83. constructor Create; virtual;
  84. destructor Destroy; override;
  85. {:Clears all data fields.}
  86. procedure Clear; virtual;
  87. {Add headers from from this object to Value.}
  88. procedure EncodeHeaders(const Value: TStrings); virtual;
  89. {:Parse header from Value to this object.}
  90. procedure DecodeHeaders(const Value: TStrings);
  91. {:Try find specific header in CustomHeader. Search is case insensitive.
  92. This is good for reading any non-parsed header.}
  93. function FindHeader(Value: string): string;
  94. {:Try find specific headers in CustomHeader. This metod is for repeatly used
  95. headers like 'received' header, etc. Search is case insensitive.
  96. This is good for reading ano non-parsed header.}
  97. procedure FindHeaderList(Value: string; const HeaderList: TStrings);
  98. published
  99. {:Sender of message.}
  100. property From: string read FFrom Write FFrom;
  101. {:Stringlist with receivers of message. (one per line)}
  102. property ToList: TStringList read FToList;
  103. {:Stringlist with Carbon Copy receivers of message. (one per line)}
  104. property CCList: TStringList read FCCList;
  105. {:Subject of message.}
  106. property Subject: string read FSubject Write FSubject;
  107. {:Organization string.}
  108. property Organization: string read FOrganization Write FOrganization;
  109. {:After decoding contains all headers lines witch not have parsed to any
  110. other structures in this object. It mean: this conatins all other headers
  111. except:
  112. X-MAILER, FROM, SUBJECT, ORGANIZATION, TO, CC, DATE, MIME-VERSION,
  113. CONTENT-TYPE, CONTENT-DESCRIPTION, CONTENT-DISPOSITION, CONTENT-ID,
  114. CONTENT-TRANSFER-ENCODING, REPLY-TO, MESSAGE-ID, X-MSMAIL-PRIORITY,
  115. X-PRIORITY, PRIORITY
  116. When you encode headers, all this lines is added as headers. Be carefull
  117. for duplicites!}
  118. property CustomHeaders: TStringList read FCustomHeaders;
  119. {:Date and time of message.}
  120. property Date: TDateTime read FDate Write FDate;
  121. {:Mailer identification.}
  122. property XMailer: string read FXMailer Write FXMailer;
  123. {:Address for replies}
  124. property ReplyTo: string read FReplyTo Write FReplyTo;
  125. {:message indetifier}
  126. property MessageID: string read FMessageID Write FMessageID;
  127. {:message priority}
  128. property Priority: TMessPriority read FPriority Write FPriority;
  129. {:Specify base charset. By default is used system charset.}
  130. property CharsetCode: TMimeChar read FCharsetCode Write FCharsetCode;
  131. end;
  132. TMessHeaderClass = class of TMessHeader;
  133. {:@abstract(Object for handling of e-mail message.)}
  134. TMimeMess = class(TObject)
  135. private
  136. FMessagePart: TMimePart;
  137. FLines: TStringList;
  138. FHeader: TMessHeader;
  139. public
  140. constructor Create;
  141. {:create this object and assign your own descendant of @link(TMessHeader)
  142. object to @link(header) property. So, you can create your own message
  143. headers parser and use it by this object.}
  144. constructor CreateAltHeaders(HeadClass: TMessHeaderClass);
  145. destructor Destroy; override;
  146. {:Reset component to default state.}
  147. procedure Clear; virtual;
  148. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  149. then set as PartParent @NIL value. If you need set more then one subpart,
  150. you must have PartParent of multipart type!}
  151. function AddPart(const PartParent: TMimePart): TMimePart;
  152. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  153. then set as PartParent @NIL value. If you need set more then 1 subpart, you
  154. must have PartParent of multipart type!
  155. This part is marked as multipart with secondary MIME type specified by
  156. MultipartType parameter. (typical value is 'mixed')
  157. This part can be used as PartParent for another parts (include next
  158. multipart). If you need only one part, then you not need Multipart part.}
  159. function AddPartMultipart(const MultipartType: String; const PartParent: TMimePart): TMimePart;
  160. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  161. then set as PartParent @NIL value. If you need set more then 1 subpart, you
  162. must have PartParent of multipart type!
  163. After creation of part set type to text part and set all necessary
  164. properties. Content of part is readed from value stringlist.}
  165. function AddPartText(const Value: TStrings; const PartParent: TMimePart): TMimepart;
  166. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  167. then set as PartParent @NIL value. If you need set more then 1 subpart, you
  168. must have PartParent of multipart type!
  169. After creation of part set type to text part and set all necessary
  170. properties. Content of part is readed from value stringlist. You can select
  171. your charset and your encoding type. If Raw is @true, then it not doing
  172. charset conversion!}
  173. function AddPartTextEx(const Value: TStrings; const PartParent: TMimePart;
  174. PartCharset: TMimeChar; Raw: Boolean; PartEncoding: TMimeEncoding): TMimepart;
  175. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  176. then set as PartParent @NIL value. If you need set more then 1 subpart, you
  177. must have PartParent of multipart type!
  178. After creation of part set type to text part to HTML type and set all
  179. necessary properties. Content of HTML part is readed from Value stringlist.}
  180. function AddPartHTML(const Value: TStrings; const PartParent: TMimePart): TMimepart;
  181. {:Same as @link(AddPartText), but content is readed from file}
  182. function AddPartTextFromFile(const FileName: String; const PartParent: TMimePart): TMimepart;
  183. {:Same as @link(AddPartHTML), but content is readed from file}
  184. function AddPartHTMLFromFile(const FileName: String; const PartParent: TMimePart): TMimepart;
  185. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  186. then set as PartParent @NIL value. If you need set more then 1 subpart,
  187. you must have PartParent of multipart type!
  188. After creation of part set type to binary and set all necessary properties.
  189. MIME primary and secondary types defined automaticly by filename extension.
  190. Content of binary part is readed from Stream. This binary part is encoded
  191. as file attachment.}
  192. function AddPartBinary(const Stream: TStream; const FileName: string; const PartParent: TMimePart): TMimepart;
  193. {:Same as @link(AddPartBinary), but content is readed from file}
  194. function AddPartBinaryFromFile(const FileName: string; const PartParent: TMimePart): TMimepart;
  195. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  196. then set as PartParent @NIL value. If you need set more then 1 subpart, you
  197. must have PartParent of multipart type!
  198. After creation of part set type to binary and set all necessary properties.
  199. MIME primary and secondary types defined automaticly by filename extension.
  200. Content of binary part is readed from Stream.
  201. This binary part is encoded as inline data with given Conten ID (cid).
  202. Content ID can be used as reference ID in HTML source in HTML part.}
  203. function AddPartHTMLBinary(const Stream: TStream; const FileName, Cid: string; const PartParent: TMimePart): TMimepart;
  204. {:Same as @link(AddPartHTMLBinary), but content is readed from file}
  205. function AddPartHTMLBinaryFromFile(const FileName, Cid: string; const PartParent: TMimePart): TMimepart;
  206. {:Add MIME part as subpart of PartParent. If you need set root MIME part,
  207. then set as PartParent @NIL value. If you need set more then 1 subpart, you
  208. must have PartParent of multipart type!
  209. After creation of part set type to message and set all necessary properties.
  210. MIME primary and secondary types are setted to 'message/rfc822'.
  211. Content of raw RFC-822 message is readed from Stream.}
  212. function AddPartMess(const Value: TStrings; const PartParent: TMimePart): TMimepart;
  213. {:Same as @link(AddPartMess), but content is readed from file}
  214. function AddPartMessFromFile(const FileName: string; const PartParent: TMimePart): TMimepart;
  215. {:Compose message from @link(MessagePart) to @link(Lines). Headers from
  216. @link(Header) object is added also.}
  217. procedure EncodeMessage; virtual;
  218. {:Decode message from @link(Lines) to @link(MessagePart). Massage headers
  219. are parsed into @link(Header) object.}
  220. procedure DecodeMessage; virtual;
  221. {pf}
  222. {: HTTP message is received by @link(THTTPSend) component in two parts:
  223. headers are stored in @link(THTTPSend.Headers) and a body in memory stream
  224. @link(THTTPSend.Document).
  225. On the top of it, HTTP connections are always 8-bit, hence data are
  226. transferred in native format i.e. no transfer encoding is applied.
  227. This method operates the similiar way and produces the same
  228. result as @link(DecodeMessage).
  229. }
  230. procedure DecodeMessageBinary(AHeader:TStrings; AData:TMemoryStream);
  231. {/pf}
  232. published
  233. {:@link(TMimePart) object with decoded MIME message. This object can handle
  234. any number of nested @link(TMimePart) objects itself. It is used for handle
  235. any tree of MIME subparts.}
  236. property MessagePart: TMimePart read FMessagePart;
  237. {:Raw MIME encoded message.}
  238. property Lines: TStringList read FLines;
  239. {:Object for e-mail header fields. This object is created automaticly.
  240. Do not free this object!}
  241. property Header: TMessHeader read FHeader;
  242. end;
  243. implementation
  244. {==============================================================================}
  245. constructor TMessHeader.Create;
  246. begin
  247. inherited Create;
  248. FToList := CreateStringList;
  249. FCCList := CreateStringList;
  250. FCustomHeaders := CreateStringList;
  251. FCharsetCode := GetCurCP;
  252. end;
  253. destructor TMessHeader.Destroy;
  254. begin
  255. FCustomHeaders.Free;
  256. FCCList.Free;
  257. FToList.Free;
  258. inherited Destroy;
  259. end;
  260. {==============================================================================}
  261. procedure TMessHeader.Clear;
  262. begin
  263. FFrom := '';
  264. FToList.Clear;
  265. FCCList.Clear;
  266. FSubject := '';
  267. FOrganization := '';
  268. FCustomHeaders.Clear;
  269. FDate := 0;
  270. FXMailer := '';
  271. FReplyTo := '';
  272. FMessageID := '';
  273. FPriority := MP_unknown;
  274. end;
  275. procedure TMessHeader.EncodeHeaders(const Value: TStrings);
  276. var
  277. n: Integer;
  278. s: string;
  279. begin
  280. if FDate = 0 then
  281. FDate := Now;
  282. for n := FCustomHeaders.Count - 1 downto 0 do
  283. if FCustomHeaders[n] <> '' then
  284. Value.Insert(0, FCustomHeaders[n]);
  285. if FPriority <> MP_unknown then
  286. case FPriority of
  287. MP_high:
  288. begin
  289. Value.Insert(0, 'X-MSMAIL-Priority: High');
  290. Value.Insert(0, 'X-Priority: 1');
  291. Value.Insert(0, 'Priority: urgent');
  292. end;
  293. MP_low:
  294. begin
  295. Value.Insert(0, 'X-MSMAIL-Priority: low');
  296. Value.Insert(0, 'X-Priority: 5');
  297. Value.Insert(0, 'Priority: non-urgent');
  298. end;
  299. end;
  300. if FReplyTo <> '' then
  301. Value.Insert(0, 'Reply-To: ' + GetEmailAddr(FReplyTo));
  302. if FMessageID <> '' then
  303. Value.Insert(0, 'Message-ID: <' + trim(FMessageID) + '>');
  304. if FXMailer = '' then
  305. Value.Insert(0, 'X-mailer: Synapse - Pascal TCP/IP library by Lukas Gebauer')
  306. else
  307. Value.Insert(0, 'X-mailer: ' + FXMailer);
  308. Value.Insert(0, 'MIME-Version: 1.0 (produced by Synapse)');
  309. if FOrganization <> '' then
  310. Value.Insert(0, 'Organization: ' + InlineCodeEx(FOrganization, FCharsetCode));
  311. s := '';
  312. for n := 0 to FCCList.Count - 1 do
  313. if s = '' then
  314. s := InlineEmailEx(FCCList[n], FCharsetCode)
  315. else
  316. s := s + ', ' + InlineEmailEx(FCCList[n], FCharsetCode);
  317. if s <> '' then
  318. Value.Insert(0, 'CC: ' + s);
  319. Value.Insert(0, 'Date: ' + Rfc822DateTime(FDate));
  320. if FSubject <> '' then
  321. Value.Insert(0, 'Subject: ' + InlineCodeEx(FSubject, FCharsetCode));
  322. s := '';
  323. for n := 0 to FToList.Count - 1 do
  324. if s = '' then
  325. s := InlineEmailEx(FToList[n], FCharsetCode)
  326. else
  327. s := s + ', ' + InlineEmailEx(FToList[n], FCharsetCode);
  328. if s <> '' then
  329. Value.Insert(0, 'To: ' + s);
  330. Value.Insert(0, 'From: ' + InlineEmailEx(FFrom, FCharsetCode));
  331. end;
  332. function TMessHeader.ParsePriority(value: string): TMessPriority;
  333. var
  334. s: string;
  335. x: integer;
  336. begin
  337. Result := MP_unknown;
  338. s := Trim(separateright(value, ':'));
  339. s := Separateleft(s, ' ');
  340. x := StrToIntDef(s, -1);
  341. if x >= 0 then
  342. case x of
  343. 1, 2:
  344. Result := MP_High;
  345. 3:
  346. Result := MP_Normal;
  347. 4, 5:
  348. Result := MP_Low;
  349. end
  350. else
  351. begin
  352. s := lowercase(s);
  353. if (s = 'urgent') or (s = 'high') or (s = 'highest') then
  354. Result := MP_High;
  355. if (s = 'normal') or (s = 'medium') then
  356. Result := MP_Normal;
  357. if (s = 'low') or (s = 'lowest')
  358. or (s = 'no-priority') or (s = 'non-urgent') then
  359. Result := MP_Low;
  360. end;
  361. end;
  362. function TMessHeader.DecodeHeader(value: string): boolean;
  363. var
  364. s, t: string;
  365. cp: TMimeChar;
  366. begin
  367. Result := True;
  368. cp := FCharsetCode;
  369. s := uppercase(value);
  370. if Pos('X-MAILER:', s) = 1 then
  371. begin
  372. FXMailer := Trim(SeparateRight(Value, ':'));
  373. Exit;
  374. end;
  375. if Pos('FROM:', s) = 1 then
  376. begin
  377. FFrom := InlineDecode(Trim(SeparateRight(Value, ':')), cp);
  378. Exit;
  379. end;
  380. if Pos('SUBJECT:', s) = 1 then
  381. begin
  382. FSubject := InlineDecode(Trim(SeparateRight(Value, ':')), cp);
  383. Exit;
  384. end;
  385. if Pos('ORGANIZATION:', s) = 1 then
  386. begin
  387. FOrganization := InlineDecode(Trim(SeparateRight(Value, ':')), cp);
  388. Exit;
  389. end;
  390. if Pos('TO:', s) = 1 then
  391. begin
  392. s := Trim(SeparateRight(Value, ':'));
  393. repeat
  394. t := InlineDecode(Trim(FetchEx(s, ',', '"')), cp);
  395. if t <> '' then
  396. FToList.Add(t);
  397. until s = '';
  398. Exit;
  399. end;
  400. if Pos('CC:', s) = 1 then
  401. begin
  402. s := Trim(SeparateRight(Value, ':'));
  403. repeat
  404. t := InlineDecode(Trim(FetchEx(s, ',', '"')), cp);
  405. if t <> '' then
  406. FCCList.Add(t);
  407. until s = '';
  408. Exit;
  409. end;
  410. if Pos('DATE:', s) = 1 then
  411. begin
  412. FDate := DecodeRfcDateTime(Trim(SeparateRight(Value, ':')));
  413. Exit;
  414. end;
  415. if Pos('REPLY-TO:', s) = 1 then
  416. begin
  417. FReplyTo := InlineDecode(Trim(SeparateRight(Value, ':')), cp);
  418. Exit;
  419. end;
  420. if Pos('MESSAGE-ID:', s) = 1 then
  421. begin
  422. FMessageID := GetEmailAddr(Trim(SeparateRight(Value, ':')));
  423. Exit;
  424. end;
  425. if Pos('PRIORITY:', s) = 1 then
  426. begin
  427. FPri := ParsePriority(value);
  428. Exit;
  429. end;
  430. if Pos('X-PRIORITY:', s) = 1 then
  431. begin
  432. FXPri := ParsePriority(value);
  433. Exit;
  434. end;
  435. if Pos('X-MSMAIL-PRIORITY:', s) = 1 then
  436. begin
  437. FXmsPri := ParsePriority(value);
  438. Exit;
  439. end;
  440. if Pos('MIME-VERSION:', s) = 1 then
  441. Exit;
  442. if Pos('CONTENT-TYPE:', s) = 1 then
  443. Exit;
  444. if Pos('CONTENT-DESCRIPTION:', s) = 1 then
  445. Exit;
  446. if Pos('CONTENT-DISPOSITION:', s) = 1 then
  447. Exit;
  448. if Pos('CONTENT-ID:', s) = 1 then
  449. Exit;
  450. if Pos('CONTENT-TRANSFER-ENCODING:', s) = 1 then
  451. Exit;
  452. Result := False;
  453. end;
  454. procedure TMessHeader.DecodeHeaders(const Value: TStrings);
  455. var
  456. s: string;
  457. x: Integer;
  458. begin
  459. Clear;
  460. Fpri := MP_unknown;
  461. Fxpri := MP_unknown;
  462. Fxmspri := MP_unknown;
  463. x := 0;
  464. while Value.Count > x do
  465. begin
  466. s := NormalizeHeader(Value, x);
  467. if s = '' then
  468. Break;
  469. if not DecodeHeader(s) then
  470. FCustomHeaders.Add(s);
  471. end;
  472. if Fpri <> MP_unknown then
  473. FPriority := Fpri
  474. else
  475. if Fxpri <> MP_unknown then
  476. FPriority := Fxpri
  477. else
  478. if Fxmspri <> MP_unknown then
  479. FPriority := Fxmspri
  480. end;
  481. function TMessHeader.FindHeader(Value: string): string;
  482. var
  483. n: integer;
  484. begin
  485. Result := '';
  486. for n := 0 to FCustomHeaders.Count - 1 do
  487. if Pos(UpperCase(Value), UpperCase(FCustomHeaders[n])) = 1 then
  488. begin
  489. Result := Trim(SeparateRight(FCustomHeaders[n], ':'));
  490. break;
  491. end;
  492. end;
  493. procedure TMessHeader.FindHeaderList(Value: string; const HeaderList: TStrings);
  494. var
  495. n: integer;
  496. begin
  497. HeaderList.Clear;
  498. for n := 0 to FCustomHeaders.Count - 1 do
  499. if Pos(UpperCase(Value), UpperCase(FCustomHeaders[n])) = 1 then
  500. begin
  501. HeaderList.Add(Trim(SeparateRight(FCustomHeaders[n], ':')));
  502. end;
  503. end;
  504. {==============================================================================}
  505. constructor TMimeMess.Create;
  506. begin
  507. CreateAltHeaders(TMessHeader);
  508. end;
  509. constructor TMimeMess.CreateAltHeaders(HeadClass: TMessHeaderClass);
  510. begin
  511. inherited Create;
  512. FMessagePart := TMimePart.Create;
  513. FLines := CreateStringList;
  514. FHeader := HeadClass.Create;
  515. end;
  516. destructor TMimeMess.Destroy;
  517. begin
  518. FMessagePart.Free;
  519. FHeader.Free;
  520. FLines.Free;
  521. inherited Destroy;
  522. end;
  523. {==============================================================================}
  524. procedure TMimeMess.Clear;
  525. begin
  526. FMessagePart.Clear;
  527. FLines.Clear;
  528. FHeader.Clear;
  529. end;
  530. {==============================================================================}
  531. function TMimeMess.AddPart(const PartParent: TMimePart): TMimePart;
  532. begin
  533. if PartParent = nil then
  534. Result := FMessagePart
  535. else
  536. Result := PartParent.AddSubPart;
  537. Result.Clear;
  538. end;
  539. {==============================================================================}
  540. function TMimeMess.AddPartMultipart(const MultipartType: String; const PartParent: TMimePart): TMimePart;
  541. begin
  542. Result := AddPart(PartParent);
  543. with Result do
  544. begin
  545. Primary := 'Multipart';
  546. Secondary := MultipartType;
  547. Description := 'Multipart message';
  548. Boundary := GenerateBoundary;
  549. EncodePartHeader;
  550. end;
  551. end;
  552. function TMimeMess.AddPartText(const Value: TStrings; const PartParent: TMimePart): TMimepart;
  553. begin
  554. Result := AddPart(PartParent);
  555. with Result do
  556. begin
  557. Value.SaveToStream(DecodedLines);
  558. Primary := 'text';
  559. Secondary := 'plain';
  560. Description := 'Message text';
  561. Disposition := 'inline';
  562. CharsetCode := IdealCharsetCoding(Value.Text, TargetCharset, IdealCharsets);
  563. EncodingCode := ME_QUOTED_PRINTABLE;
  564. EncodePart;
  565. EncodePartHeader;
  566. end;
  567. end;
  568. function TMimeMess.AddPartTextEx(const Value: TStrings; const PartParent: TMimePart;
  569. PartCharset: TMimeChar; Raw: Boolean; PartEncoding: TMimeEncoding): TMimepart;
  570. begin
  571. Result := AddPart(PartParent);
  572. with Result do
  573. begin
  574. Value.SaveToStream(DecodedLines);
  575. Primary := 'text';
  576. Secondary := 'plain';
  577. Description := 'Message text';
  578. Disposition := 'inline';
  579. CharsetCode := PartCharset;
  580. EncodingCode := PartEncoding;
  581. ConvertCharset := not Raw;
  582. EncodePart;
  583. EncodePartHeader;
  584. end;
  585. end;
  586. function TMimeMess.AddPartHTML(const Value: TStrings; const PartParent: TMimePart): TMimepart;
  587. begin
  588. Result := AddPart(PartParent);
  589. with Result do
  590. begin
  591. Value.SaveToStream(DecodedLines);
  592. Primary := 'text';
  593. Secondary := 'html';
  594. Description := 'HTML text';
  595. Disposition := 'inline';
  596. CharsetCode := UTF_8;
  597. EncodingCode := ME_QUOTED_PRINTABLE;
  598. EncodePart;
  599. EncodePartHeader;
  600. end;
  601. end;
  602. function TMimeMess.AddPartTextFromFile(const FileName: String; const PartParent: TMimePart): TMimepart;
  603. var
  604. tmp: TStrings;
  605. begin
  606. tmp := CreateStringList;
  607. try
  608. tmp.LoadFromFile(FileName);
  609. Result := AddPartText(tmp, PartParent);
  610. Finally
  611. tmp.Free;
  612. end;
  613. end;
  614. function TMimeMess.AddPartHTMLFromFile(const FileName: String; const PartParent: TMimePart): TMimepart;
  615. var
  616. tmp: TStrings;
  617. begin
  618. tmp := CreateStringList;
  619. try
  620. tmp.LoadFromFile(FileName);
  621. Result := AddPartHTML(tmp, PartParent);
  622. Finally
  623. tmp.Free;
  624. end;
  625. end;
  626. function TMimeMess.AddPartBinary(const Stream: TStream; const FileName: string; const PartParent: TMimePart): TMimepart;
  627. begin
  628. Result := AddPart(PartParent);
  629. Result.DecodedLines.LoadFromStream(Stream);
  630. Result.MimeTypeFromExt(FileName);
  631. Result.Description := 'Attached file: ' + FileName;
  632. Result.Disposition := 'attachment';
  633. Result.FileName := FileName;
  634. Result.EncodingCode := ME_BASE64;
  635. Result.EncodePart;
  636. Result.EncodePartHeader;
  637. end;
  638. function TMimeMess.AddPartBinaryFromFile(const FileName: string; const PartParent: TMimePart): TMimepart;
  639. var
  640. tmp: TMemoryStream;
  641. begin
  642. tmp := TMemoryStream.Create;
  643. try
  644. tmp.LoadFromFile(FileName);
  645. Result := AddPartBinary(tmp, ExtractFileName(FileName), PartParent);
  646. finally
  647. tmp.Free;
  648. end;
  649. end;
  650. function TMimeMess.AddPartHTMLBinary(const Stream: TStream; const FileName, Cid: string; const PartParent: TMimePart): TMimepart;
  651. begin
  652. Result := AddPart(PartParent);
  653. Result.DecodedLines.LoadFromStream(Stream);
  654. Result.MimeTypeFromExt(FileName);
  655. Result.Description := 'Included file: ' + FileName;
  656. Result.Disposition := 'inline';
  657. Result.ContentID := Cid;
  658. Result.FileName := FileName;
  659. Result.EncodingCode := ME_BASE64;
  660. Result.EncodePart;
  661. Result.EncodePartHeader;
  662. end;
  663. function TMimeMess.AddPartHTMLBinaryFromFile(const FileName, Cid: string; const PartParent: TMimePart): TMimepart;
  664. var
  665. tmp: TMemoryStream;
  666. begin
  667. tmp := TMemoryStream.Create;
  668. try
  669. tmp.LoadFromFile(FileName);
  670. Result :=AddPartHTMLBinary(tmp, ExtractFileName(FileName), Cid, PartParent);
  671. finally
  672. tmp.Free;
  673. end;
  674. end;
  675. function TMimeMess.AddPartMess(const Value: TStrings; const PartParent: TMimePart): TMimepart;
  676. var
  677. part: Tmimepart;
  678. begin
  679. Result := AddPart(PartParent);
  680. part := AddPart(result);
  681. part.lines.addstrings(Value);
  682. part.DecomposeParts;
  683. with Result do
  684. begin
  685. Primary := 'message';
  686. Secondary := 'rfc822';
  687. Description := 'E-mail Message';
  688. EncodePart;
  689. EncodePartHeader;
  690. end;
  691. end;
  692. function TMimeMess.AddPartMessFromFile(const FileName: String; const PartParent: TMimePart): TMimepart;
  693. var
  694. tmp: TStrings;
  695. begin
  696. tmp := CreateStringList;
  697. try
  698. tmp.LoadFromFile(FileName);
  699. Result := AddPartMess(tmp, PartParent);
  700. Finally
  701. tmp.Free;
  702. end;
  703. end;
  704. {==============================================================================}
  705. procedure TMimeMess.EncodeMessage;
  706. var
  707. l: TStringList;
  708. x: integer;
  709. begin
  710. //merge headers from THeaders and header field from MessagePart
  711. l := CreateStringList;
  712. try
  713. FHeader.EncodeHeaders(l);
  714. x := IndexByBegin('CONTENT-TYPE', FMessagePart.Headers);
  715. if x >= 0 then
  716. l.add(FMessagePart.Headers[x]);
  717. x := IndexByBegin('CONTENT-DESCRIPTION', FMessagePart.Headers);
  718. if x >= 0 then
  719. l.add(FMessagePart.Headers[x]);
  720. x := IndexByBegin('CONTENT-DISPOSITION', FMessagePart.Headers);
  721. if x >= 0 then
  722. l.add(FMessagePart.Headers[x]);
  723. x := IndexByBegin('CONTENT-ID', FMessagePart.Headers);
  724. if x >= 0 then
  725. l.add(FMessagePart.Headers[x]);
  726. x := IndexByBegin('CONTENT-TRANSFER-ENCODING', FMessagePart.Headers);
  727. if x >= 0 then
  728. l.add(FMessagePart.Headers[x]);
  729. FMessagePart.Headers.Assign(l);
  730. finally
  731. l.Free;
  732. end;
  733. FMessagePart.ComposeParts;
  734. FLines.Assign(FMessagePart.Lines);
  735. end;
  736. {==============================================================================}
  737. procedure TMimeMess.DecodeMessage;
  738. begin
  739. FHeader.Clear;
  740. FHeader.DecodeHeaders(FLines);
  741. FMessagePart.Lines.Assign(FLines);
  742. FMessagePart.DecomposeParts;
  743. end;
  744. {pf}
  745. procedure TMimeMess.DecodeMessageBinary(AHeader:TStrings; AData:TMemoryStream);
  746. begin
  747. FHeader.Clear;
  748. FLines.Clear;
  749. FLines.Assign(AHeader);
  750. FHeader.DecodeHeaders(FLines);
  751. FMessagePart.DecomposePartsBinary(AHeader,PChar(AData.Memory),PChar(AData.Memory)+AData.Size);
  752. end;
  753. {/pf}
  754. end.