IdMessageBuilder.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838
  1. unit IdMessageBuilder;
  2. interface
  3. {$i IdCompilerDefines.inc}
  4. uses
  5. Classes, IdMessage;
  6. type
  7. TIdMessageBuilderAttachment = class(TCollectionItem)
  8. private
  9. FContentID: String;
  10. FContentTransfer: String;
  11. FContentType: String;
  12. FData: TStream;
  13. FFileName: String;
  14. FName: String;
  15. FWantedFileName: String;
  16. public
  17. procedure Assign(Source: TPersistent); override;
  18. property ContentID: String read FContentID write FContentID;
  19. property ContentTransfer: String read FContentTransfer write FContentTransfer;
  20. property ContentType: String read FContentType write FContentType;
  21. property Data: TStream read FData write FData;
  22. property FileName: String read FFileName write FFileName;
  23. property Name: String read FName write FName;
  24. property WantedFileName: String read FWantedFileName write FWantedFileName;
  25. end;
  26. TIdMessageBuilderAttachments = class(TCollection)
  27. private
  28. function GetAttachment(Index: Integer): TIdMessageBuilderAttachment;
  29. procedure SetAttachment(Index: Integer; Value: TIdMessageBuilderAttachment);
  30. public
  31. constructor Create; reintroduce;
  32. function Add: TIdMessageBuilderAttachment; reintroduce; overload;
  33. function Add(const AFileName: String; const AContentID: String = ''): TIdMessageBuilderAttachment; overload;
  34. function Add(AData: TStream; const AContentType: String; const AContentID: String = ''): TIdMessageBuilderAttachment; overload;
  35. procedure AddToMessage(AMsg: TIdMessage; ParentPart: Integer);
  36. property Attachment[Index: Integer]: TIdMessageBuilderAttachment
  37. read GetAttachment write SetAttachment; default;
  38. end;
  39. TIdCustomMessageBuilder = class
  40. protected
  41. FAttachments: TIdMessageBuilderAttachments;
  42. FPlainText: TStrings;
  43. FPlainTextCharSet: String;
  44. FPlainTextContentTransfer: String;
  45. procedure AddAttachments(AMsg: TIdMessage);
  46. procedure FillBody(AMsg: TIdMessage); virtual; abstract;
  47. procedure FillHeaders(AMsg: TIdMessage); virtual;
  48. procedure SetPlainText(AValue: TStrings);
  49. procedure SetAttachments(AValue: TIdMessageBuilderAttachments);
  50. public
  51. constructor Create; virtual;
  52. destructor Destroy; override;
  53. //
  54. procedure Clear; virtual;
  55. procedure FillMessage(AMsg: TIdMessage);
  56. function NewMessage(AOwner: TComponent = nil): TIdMessage;
  57. //
  58. property Attachments: TIdMessageBuilderAttachments read FAttachments write SetAttachments;
  59. property PlainText: TStrings read FPlainText write SetPlainText;
  60. property PlainTextCharSet: String read FPlainTextCharSet write FPlainTextCharSet;
  61. property PlainTextContentTransfer: String read FPlainTextContentTransfer write FPlainTextContentTransfer;
  62. end;
  63. TIdMessageBuilderPlain = class(TIdCustomMessageBuilder)
  64. protected
  65. procedure FillBody(AMsg: TIdMessage); override;
  66. procedure FillHeaders(AMsg: TIdMessage); override;
  67. end;
  68. TIdMessageBuilderHtml = class(TIdCustomMessageBuilder)
  69. protected
  70. FHtml: TStrings;
  71. FHtmlCharSet: String;
  72. FHtmlContentTransfer: String;
  73. FHtmlFiles: TIdMessageBuilderAttachments;
  74. FHtmlViewerNeededMsg: String;
  75. procedure FillBody(AMsg: TIdMessage); override;
  76. procedure FillHeaders(AMsg: TIdMessage); override;
  77. procedure SetHtml(AValue: TStrings);
  78. procedure SetHtmlFiles(AValue: TIdMessageBuilderAttachments);
  79. public
  80. constructor Create; override;
  81. destructor Destroy; override;
  82. //
  83. procedure Clear; override;
  84. //
  85. property Html: TStrings read FHtml write SetHtml;
  86. property HtmlCharSet: String read FHtmlCharSet write FHtmlCharSet;
  87. property HtmlContentTransfer: String read FHtmlContentTransfer write FHtmlContentTransfer;
  88. property HtmlFiles: TIdMessageBuilderAttachments read FHtmlFiles write SetHtmlFiles;
  89. property HtmlViewerNeededMsg: String read FHtmlViewerNeededMsg write FHtmlViewerNeededMsg;
  90. end;
  91. TIdMessageBuilderRtfType = (idMsgBldrRtfMS, idMsgBldrRtfEnriched, idMsgBldrRtfRichtext);
  92. TIdMessageBuilderRtf = class(TIdCustomMessageBuilder)
  93. protected
  94. FRtf: TStrings;
  95. FRtfType: TIdMessageBuilderRtfType;
  96. FRtfViewerNeededMsg: String;
  97. procedure FillBody(AMsg: TIdMessage); override;
  98. procedure FillHeaders(AMsg: TIdMessage); override;
  99. procedure SetRtf(AValue: TStrings);
  100. public
  101. constructor Create; override;
  102. destructor Destroy; override;
  103. //
  104. procedure Clear; override;
  105. //
  106. property Rtf: TStrings read FRtf write SetRtf;
  107. property RtfType: TIdMessageBuilderRtfType read FRtfType write FRtfType;
  108. property RtfViewerNeededMsg: String read FRtfViewerNeededMsg write FRtfViewerNeededMsg;
  109. end;
  110. implementation
  111. uses
  112. IdGlobal, IdGlobalProtocols, IdMessageParts, IdAttachment, IdAttachmentFile,
  113. IdAttachmentMemory, IdResourceStringsProtocols, IdText, SysUtils;
  114. const
  115. cTextPlain = 'text/plain'; {do not localize}
  116. cTextHtml = 'text/html'; {do not localize}
  117. cTextRtf: array[TIdMessageBuilderRtfType] of String = ('text/rtf', 'text/enriched', 'text/richtext'); {do not localize}
  118. cMultipartAlternative = 'multipart/alternative'; {do not localize}
  119. cMultipartMixed = 'multipart/mixed'; {do not localize}
  120. cMultipartRelatedHtml = 'multipart/related; type="text/html"'; {do not localize}
  121. cQuotedPrintable = 'quoted-printable'; {do not localize}
  122. cUTF8 = 'utf-8'; {do not localize}
  123. { TIdMessageBuilderAttachment }
  124. procedure TIdMessageBuilderAttachment.Assign(Source: TPersistent);
  125. var
  126. LSource: TIdMessageBuilderAttachment;
  127. begin
  128. if Source is TIdMessageBuilderAttachment then
  129. begin
  130. LSource := TIdMessageBuilderAttachment(Source);
  131. FContentID := LSource.FContentID;
  132. FContentTransfer := LSource.FContentTransfer;
  133. FContentType := LSource.FContentType;
  134. FData := LSource.FData;
  135. FFileName := LSource.FFileName;
  136. FName := LSource.FName;
  137. FWantedFileName := LSource.FWantedFileName;
  138. end else begin
  139. inherited Assign(Source);
  140. end;
  141. end;
  142. { TIdMessageBuilderAttachments }
  143. constructor TIdMessageBuilderAttachments.Create;
  144. begin
  145. inherited Create(TIdMessageBuilderAttachment);
  146. end;
  147. function TIdMessageBuilderAttachments.Add: TIdMessageBuilderAttachment;
  148. begin
  149. // This helps prevent unsupported TIdMessageBuilderAttachment from being added
  150. Result := nil;
  151. end;
  152. function TIdMessageBuilderAttachments.Add(const AFileName: String;
  153. const AContentID: String = ''): TIdMessageBuilderAttachment;
  154. begin
  155. Result := TIdMessageBuilderAttachment(inherited Add);
  156. if AContentID <> '' then begin
  157. Result.FContentID := AContentID;
  158. end else begin
  159. Result.FContentID := ExtractFileName(AFileName);
  160. end;
  161. Result.FFileName := AFileName;
  162. Result.FWantedFileName := ExtractFileName(AFileName);
  163. end;
  164. function TIdMessageBuilderAttachments.Add(AData: TStream; const AContentType: String;
  165. const AContentID: String = ''): TIdMessageBuilderAttachment;
  166. begin
  167. Assert(AData <> nil);
  168. Result := TIdMessageBuilderAttachment(inherited Add);
  169. Result.FContentID := AContentID;
  170. Result.FContentType := AContentType;
  171. Result.FData := AData;
  172. end;
  173. procedure TIdMessageBuilderAttachments.AddToMessage(AMsg: TIdMessage; ParentPart: Integer);
  174. var
  175. I: Integer;
  176. LMsgBldrAttachment: TIdMessageBuilderAttachment;
  177. LMsgAttachment: TIdAttachment;
  178. LStream: TStream;
  179. function FormatContentId(Item: TIdMessageBuilderAttachment): String;
  180. begin
  181. if Item.ContentID <> '' then begin
  182. Result := EnsureMsgIDBrackets(Item.ContentID);
  183. end else begin
  184. Result := '';
  185. end;
  186. end;
  187. function FormatContentType(Item: TIdMessageBuilderAttachment): String;
  188. begin
  189. if Item.ContentType <> '' then begin
  190. Result := Item.ContentType;
  191. end else begin
  192. Result := GetMIMETypeFromFile(Item.FileName);
  193. end;
  194. end;
  195. function FormatFileName(Item: TIdMessageBuilderAttachment): String;
  196. begin
  197. if Item.WantedFileName <> '' then begin
  198. Result := ExtractFileName(Item.WantedFileName);
  199. end
  200. else if Item.FileName <> '' then begin
  201. Result := ExtractFileName(Item.FileName);
  202. end else begin
  203. Result := '';
  204. end;
  205. end;
  206. function FormatName(Item: TIdMessageBuilderAttachment): String;
  207. begin
  208. if Item.Name <> '' then begin
  209. Result := Item.Name;
  210. end else begin
  211. Result := FormatFileName(Item);
  212. end;
  213. end;
  214. begin
  215. for I := 0 to Count-1 do
  216. begin
  217. LMsgBldrAttachment := Attachment[I];
  218. if Assigned(LMsgBldrAttachment.Data) then
  219. begin
  220. LMsgAttachment := TIdAttachmentMemory.Create(AMsg.MessageParts);
  221. try
  222. LMsgAttachment.FileName := FormatFileName(LMsgBldrAttachment);
  223. LStream := LMsgAttachment.PrepareTempStream;
  224. try
  225. LStream.CopyFrom(LMsgBldrAttachment.Data, 0);
  226. finally
  227. LMsgAttachment.FinishTempStream;
  228. end;
  229. except
  230. LMsgAttachment.Free;
  231. raise;
  232. end;
  233. end else
  234. begin
  235. LMsgAttachment := TIdAttachmentFile.Create(AMsg.MessageParts, LMsgBldrAttachment.FileName);
  236. if LMsgBldrAttachment.WantedFileName <> '' then begin
  237. LMsgAttachment.FileName := ExtractFileName(LMsgBldrAttachment.WantedFileName);
  238. end;
  239. end;
  240. LMsgAttachment.Name := FormatName(LMsgBldrAttachment);
  241. LMsgAttachment.ContentId := FormatContentId(LMsgBldrAttachment);
  242. LMsgAttachment.ContentType := FormatContentType(LMsgBldrAttachment);
  243. LMsgAttachment.ContentTransfer := LMsgBldrAttachment.ContentTransfer;
  244. if ParentPart > -1 then
  245. begin
  246. if IsHeaderMediaType(LMsgAttachment.ContentType, 'image') then begin {do not localize}
  247. LMsgAttachment.ContentDisposition := 'inline'; {do not localize}
  248. end;
  249. LMsgAttachment.ParentPart := ParentPart;
  250. end;
  251. end;
  252. end;
  253. function TIdMessageBuilderAttachments.GetAttachment(Index: Integer): TIdMessageBuilderAttachment;
  254. begin
  255. Result := TIdMessageBuilderAttachment(inherited GetItem(Index));
  256. end;
  257. procedure TIdMessageBuilderAttachments.SetAttachment(Index: Integer; Value: TIdMessageBuilderAttachment);
  258. begin
  259. inherited SetItem(Index, Value);
  260. end;
  261. { TIdCustomMessageBuilder }
  262. constructor TIdCustomMessageBuilder.Create;
  263. begin
  264. inherited Create;
  265. FPlainText := TStringList.Create;
  266. FAttachments := TIdMessageBuilderAttachments.Create;
  267. end;
  268. destructor TIdCustomMessageBuilder.Destroy;
  269. begin
  270. FPlainText.Free;
  271. FAttachments.Free;
  272. inherited Destroy;
  273. end;
  274. procedure TIdCustomMessageBuilder.AddAttachments(AMsg: TIdMessage);
  275. begin
  276. FAttachments.AddToMessage(AMsg, -1);
  277. end;
  278. procedure TIdCustomMessageBuilder.Clear;
  279. begin
  280. FAttachments.Clear;
  281. FPlainText.Clear;
  282. FPlainTextCharSet := '';
  283. FPlainTextContentTransfer := '';
  284. end;
  285. procedure TIdCustomMessageBuilder.FillMessage(AMsg: TIdMessage);
  286. begin
  287. if not Assigned(AMsg) then begin
  288. Exit;
  289. end;
  290. // Clear only the body, ContentType, CharSet, and ContentTransferEncoding here...
  291. //
  292. AMsg.ClearBody;
  293. AMsg.ContentType := '';
  294. AMsg.CharSet := '';
  295. AMsg.ContentTransferEncoding := '';
  296. // let the message decide how to encode itself
  297. // based on what parts are added in InternalFill()
  298. //
  299. AMsg.Encoding := meDefault;
  300. // fill in type-specific content first
  301. //
  302. FillBody(AMsg);
  303. // Are non-related attachments present?
  304. //
  305. AddAttachments(AMsg);
  306. // Determine the top-level ContentType and
  307. // ContentTransferEncoding for the message now
  308. //
  309. FillHeaders(AMsg);
  310. end;
  311. function TIdCustomMessageBuilder.NewMessage(AOwner: TComponent = nil): TIdMessage;
  312. begin
  313. Result := TIdMessage.Create(AOwner);
  314. try
  315. FillMessage(Result);
  316. except
  317. Result.Free;
  318. raise;
  319. end;
  320. end;
  321. procedure TIdCustomMessageBuilder.SetAttachments(AValue: TIdMessageBuilderAttachments);
  322. begin
  323. FAttachments.Assign(AValue);
  324. end;
  325. procedure TIdCustomMessageBuilder.FillHeaders(AMsg: TIdMessage);
  326. var
  327. LPart: TIdMessagePart;
  328. begin
  329. if FAttachments.Count > 0 then
  330. begin
  331. if AMsg.MessageParts.Count > 1 then
  332. begin
  333. // plain text and/or formatting, and at least 1 non-related attachment
  334. //
  335. AMsg.ContentType := cMultipartMixed;
  336. AMsg.CharSet := '';
  337. AMsg.ContentTransferEncoding := '';
  338. end else
  339. begin
  340. // no plain text or formatting, only 1 non-related attachment
  341. //
  342. // TODO: can we use AMsg.IsMsgSinglePartMime=True instead?
  343. //
  344. LPart := AMsg.MessageParts[0];
  345. AMsg.ContentType := LPart.ContentType;
  346. AMsg.CharSet := LPart.CharSet;
  347. AMsg.ContentTransferEncoding := LPart.ContentTransfer;
  348. end;
  349. end else
  350. begin
  351. AMsg.ContentType := '';
  352. AMsg.CharSet := '';
  353. AMsg.ContentTransferEncoding := '';
  354. end;
  355. end;
  356. procedure TIdCustomMessageBuilder.SetPlainText(AValue: TStrings);
  357. begin
  358. FPlainText.Assign(AValue);
  359. end;
  360. { TIdMessageBuilderPlain }
  361. procedure TIdMessageBuilderPlain.FillBody(AMsg: TIdMessage);
  362. var
  363. LTextPart: TIdText;
  364. begin
  365. // Is plain text present?
  366. //
  367. if FPlainText.Count > 0 then
  368. begin
  369. // Should the message contain only plain text?
  370. //
  371. if FAttachments.Count = 0 then
  372. begin
  373. AMsg.Body.Assign(FPlainText);
  374. end else
  375. begin
  376. // At this point, multiple pieces will be present in the message
  377. // body, so everything must be stored in the MessageParts collection...
  378. //
  379. LTextPart := TIdText.Create(AMsg.MessageParts, FPlainText);
  380. LTextPart.ContentType := cTextPlain;
  381. LTextPart.CharSet := FPlainTextCharSet;
  382. if LTextPart.CharSet = '' then begin
  383. LTextPart.CharSet := cUTF8;
  384. end;
  385. LTextPart.ContentTransfer := FPlainTextContentTransfer;
  386. if LTextPart.ContentTransfer = '' then begin
  387. LTextPart.ContentTransfer := cQuotedPrintable;
  388. end;
  389. end;
  390. end;
  391. end;
  392. procedure TIdMessageBuilderPlain.FillHeaders(AMsg: TIdMessage);
  393. begin
  394. if (FPlainText.Count > 0) and (FAttachments.Count = 0) then
  395. begin
  396. // plain text only
  397. //
  398. AMsg.ContentType := cTextPlain;
  399. AMsg.CharSet := FPlainTextCharSet;
  400. if AMsg.CharSet = '' then begin
  401. AMsg.CharSet := cUTF8;
  402. end;
  403. AMsg.ContentTransferEncoding := FPlainTextContentTransfer;
  404. if AMsg.ContentTransferEncoding = '' then begin
  405. AMsg.ContentTransferEncoding := cQuotedPrintable;
  406. end;
  407. end else
  408. begin
  409. inherited FillHeaders(AMsg);
  410. end;
  411. end;
  412. { TIdMessageBuilderHtml }
  413. constructor TIdMessageBuilderHtml.Create;
  414. begin
  415. inherited Create;
  416. FHtml := TStringList.Create;
  417. FHtmlFiles := TIdMessageBuilderAttachments.Create;
  418. FHtmlViewerNeededMsg := rsHtmlViewerNeeded;
  419. end;
  420. destructor TIdMessageBuilderHtml.Destroy;
  421. begin
  422. FHtml.Free;
  423. FHtmlFiles.Free;
  424. inherited Destroy;
  425. end;
  426. procedure TIdMessageBuilderHtml.Clear;
  427. begin
  428. FHtml.Clear;
  429. FHtmlCharSet := '';
  430. FHtmlContentTransfer := '';
  431. FHtmlFiles.Clear;
  432. inherited Clear;
  433. end;
  434. procedure TIdMessageBuilderHtml.FillBody(AMsg: TIdMessage);
  435. var
  436. LUsePlain, LUseHtml, LUseViewerNeededMsg, LUseHtmlFiles, LUseAttachments: Boolean;
  437. LAlternativeIndex, LRelatedIndex: Integer;
  438. LTextPart: TIdText;
  439. begin
  440. // Cache these for better performance
  441. //
  442. LUsePlain := FPlainText.Count > 0;
  443. LUseHtml := FHtml.Count > 0;
  444. LUseViewerMsg := FHtmlViewerNeededMsg <> '';
  445. LUseHtmlFiles := LUseHtml and (FHtmlFiles.Count > 0);
  446. LUseAttachments := FAttachments.Count > 0;
  447. LAlternativeIndex := -1;
  448. LRelatedIndex := -1;
  449. // Is any body data present at all?
  450. //
  451. if not (LUsePlain or LUseHtml or LUseHtmlFiles or LUseAttachments) then begin
  452. Exit;
  453. end;
  454. // Should the message contain only plain text?
  455. //
  456. if LUsePlain and not (LUseHtml or LUseAttachments) then
  457. begin
  458. AMsg.Body.Assign(FPlainText);
  459. Exit;
  460. end;
  461. // Should the message contain only HTML?
  462. //
  463. if LUseHtml and not (LUsePlain or LUseViewerMsg or LUseHtmlFiles or LUseAttachments) then
  464. begin
  465. // TODO: create "multipart/alternative" pieces if FHtmlViewerNeededMsg is not empty...
  466. AMsg.Body.Assign(FHtml);
  467. Exit;
  468. end;
  469. // At this point, multiple pieces will be present in the message
  470. // body, so everything must be stored in the MessageParts collection...
  471. // If the message should contain both plain text and HTML, a
  472. // "multipart/alternative" piece is needed to wrap them if
  473. // non-related attachments are also present...
  474. //
  475. // RLebeau 5/23/2011: need to output the Alternative piece if
  476. // the "HTML Viewer is needed" text is going to be used...
  477. //
  478. if {LUsePlain and} LUseHtml and LUseAttachments then
  479. begin
  480. LTextPart := TIdText.Create(AMsg.MessageParts, nil);
  481. LTextPart.ContentType := cMultipartAlternative;
  482. LAlternativeIndex := LTextPart.Index;
  483. end;
  484. // Is plain text present?
  485. //
  486. if LUsePlain or LUseHtml then
  487. begin
  488. LTextPart := TIdText.Create(AMsg.MessageParts, FPlainText);
  489. if LUseHtml and (not LUsePlain) then begin
  490. LTextPart.Body.Text := FHtmlViewerNeededMsg;
  491. end;
  492. LTextPart.ContentType := cTextPlain;
  493. LTextPart.CharSet := FPlainTextCharSet;
  494. if LTextPart.CharSet = '' then begin
  495. LTextPart.CharSet := cUTF8;
  496. end;
  497. LTextPart.ContentTransfer := FPlainTextContentTransfer;
  498. if LTextPart.ContentTransfer = '' then begin
  499. LTextPart.ContentTransfer := cQuotedPrintable;
  500. end;
  501. LTextPart.ParentPart := LAlternativeIndex;
  502. end;
  503. // Is HTML present?
  504. //
  505. if LUseHtml then
  506. begin
  507. // related attachments can't be referenced by, or used inside
  508. // of, plain text, so there is no point in wrapping the plain
  509. // text inside the same "multipart/related" part with the HTML
  510. // and attachments. Some email programs don't do that as well.
  511. // This logic is newer and more accurate than what is described
  512. // in the "HTML Messages" article found on Indy's website.
  513. //
  514. if LUseHtmlFiles then
  515. begin
  516. LTextPart := TIdText.Create(AMsg.MessageParts, nil);
  517. LTextPart.ContentType := cMultipartRelatedHtml;
  518. LTextPart.ParentPart := LAlternativeIndex;
  519. LRelatedIndex := LTextPart.Index;
  520. end;
  521. // Add HTML
  522. //
  523. LTextPart := TIdText.Create(AMsg.MessageParts, FHtml);
  524. LTextPart.ContentType := cTextHtml;
  525. LTextPart.CharSet := FHtmlCharSet;
  526. if LTextPart.CharSet = '' then begin
  527. LTextPart.CharSet := cUTF8;
  528. end;
  529. LTextPart.ContentTransfer := FHtmlContentTransfer;
  530. if LTextPart.ContentTransfer = '' then begin
  531. LTextPart.ContentTransfer := cQuotedPrintable;
  532. end;
  533. if LRelatedIndex <> -1 then begin
  534. LTextPart.ParentPart := LRelatedIndex; // plain text and related attachments
  535. end else begin
  536. LTextPart.ParentPart := LAlternativeIndex; // plain text and optional non-related attachments
  537. end;
  538. // Are related attachments present?
  539. //
  540. if LUseHtmlFiles then begin
  541. FHtmlFiles.AddToMessage(AMsg, LRelatedIndex);
  542. end;
  543. end;
  544. end;
  545. procedure TIdMessageBuilderHtml.FillHeaders(AMsg: TIdMessage);
  546. var
  547. LUsePlain, LUseHtml, LUseViewerNeededMsg: Boolean;
  548. begin
  549. if FAttachments.Count = 0 then
  550. begin
  551. LUsePlain := FPlainText.Count > 0;
  552. LUseHtml := FHtml.Count > 0;
  553. LUseViewerNeededMsg := FHtmlViewerNeededMsg <> '';
  554. if LUsePlain and (not LUseHtml) then
  555. begin
  556. // plain text only
  557. //
  558. AMsg.ContentType := cTextPlain;
  559. AMsg.CharSet := FPlainTextCharSet;
  560. if AMsg.CharSet = '' then begin
  561. AMsg.CharSet := cUTF8;
  562. end;
  563. AMsg.ContentTransferEncoding := FPlainTextContentTransfer;
  564. if AMsg.ContentTransferEncoding = '' then begin
  565. AMsg.ContentTransferEncoding := cQuotedPrintable;
  566. end;
  567. end
  568. else if LUseHtml then
  569. begin
  570. if (not LUsePlain) and (not LUseViewerNeededMsg) and (FHtmlFiles.Count = 0) then
  571. begin
  572. // HTML only
  573. //
  574. AMsg.ContentType := cTextHtml;
  575. AMsg.CharSet := FHtmlCharSet;
  576. if AMsg.CharSet = '' then begin
  577. AMsg.CharSet := cUTF8;
  578. end;
  579. AMsg.ContentTransferEncoding := FHtmlContentTransfer;
  580. if AMsg.ContentTransferEncoding = '' then begin
  581. AMsg.ContentTransferEncoding := cQuotedPrintable;
  582. end;
  583. end else
  584. begin
  585. // plain text and HTML and no related attachments
  586. //
  587. AMsg.ContentType := cMultipartAlternative;
  588. AMsg.CharSet := '';
  589. AMsg.ContentTransferEncoding := '';
  590. end;
  591. end else
  592. begin
  593. // TODO: what to put here??
  594. end;
  595. end else
  596. begin
  597. inherited FillHeaders(AMsg);
  598. end;
  599. end;
  600. procedure TIdMessageBuilderHtml.SetHtml(AValue: TStrings);
  601. begin
  602. FHtml.Assign(AValue);
  603. end;
  604. procedure TIdMessageBuilderHtml.SetHtmlFiles(AValue: TIdMessageBuilderAttachments);
  605. begin
  606. FHtmlFiles.Assign(AValue);
  607. end;
  608. { TIdMessageBuilderRTF }
  609. constructor TIdMessageBuilderRtf.Create;
  610. begin
  611. inherited Create;
  612. FRtf := TStringList.Create;
  613. FRtfType := idMsgBldrRtfMS;
  614. FRtfViewerNeededMsg := rsRtfViewerNeeded;
  615. end;
  616. destructor TIdMessageBuilderRtf.Destroy;
  617. begin
  618. FRtf.Free;
  619. inherited Destroy;
  620. end;
  621. procedure TIdMessageBuilderRtf.Clear;
  622. begin
  623. FRtf.Clear;
  624. inherited Clear;
  625. end;
  626. procedure TIdMessageBuilderRtf.FillBody(AMsg: TIdMessage);
  627. var
  628. LUsePlain, LUseRtf, LUseViewerNeededMsg, LUseAttachments: Boolean;
  629. LAlternativeIndex: Integer;
  630. LTextPart: TIdText;
  631. begin
  632. // Cache these for better performance
  633. //
  634. LUsePlain := FPlainText.Count > 0;
  635. LUseRtf := FRtf.Count > 0;
  636. LUseViewerNeededMsg := FRtfViewerNeededMsg <> '';
  637. LUseAttachments := FAttachments.Count > 0;
  638. LAlternativeIndex := -1;
  639. // Is any body data present at all?
  640. //
  641. if not (LUsePlain or LUseRtf or LUseAttachments) then begin
  642. Exit;
  643. end;
  644. // Should the message contain only plain text?
  645. //
  646. if LUsePlain and not (LUseRtf or LUseAttachments) then
  647. begin
  648. AMsg.Body.Assign(FPlainText);
  649. Exit;
  650. end;
  651. // Should the message contain only RTF?
  652. //
  653. if LUseRtf and not (LUsePlain or LUseViewerNeededMsg or LUseAttachments) then
  654. begin
  655. // TODO: create "multipart/alternative" pieces if FRtfViewerNeededMsg is not empty...
  656. AMsg.Body.Assign(FRtf);
  657. Exit;
  658. end;
  659. // At this point, multiple pieces will be present in the message
  660. // body, so everything must be stored in the MessageParts collection...
  661. // If the message should contain both plain text and RTF, a
  662. // "multipart/alternative" piece is needed to wrap them if
  663. // attachments are also present...
  664. //
  665. // RLebeau 11/11/2013: need to output the Alternative piece if
  666. // the "RTF Viewer is needed" text is going to be used...
  667. //
  668. if {LUsePlain and} LUseRtf and LUseAttachments then
  669. begin
  670. LTextPart := TIdText.Create(AMsg.MessageParts, nil);
  671. LTextPart.ContentType := cMultipartAlternative;
  672. LAlternativeIndex := LTextPart.Index;
  673. end;
  674. // Is plain text present?
  675. //
  676. if LUsePlain or LUseRtf then
  677. begin
  678. LTextPart := TIdText.Create(AMsg.MessageParts, FPlainText);
  679. if LUseRtf and (not LUsePlain) then begin
  680. LTextPart.Body.Text := FRtfViewerNeededMsg;
  681. end;
  682. LTextPart.ContentType := cTextPlain;
  683. LTextPart.CharSet := FPlainTextCharSet;
  684. if LTextPart.CharSet = '' then begin
  685. LTextPart.CharSet := cUTF8;
  686. end;
  687. LTextPart.ContentTransfer := FPlainTextContentTransfer;
  688. if LTextPart.ContentTransfer = '' then begin
  689. LTextPart.ContentTransfer := cQuotedPrintable;
  690. end;
  691. LTextPart.ParentPart := LAlternativeIndex;
  692. end;
  693. // Is RTF present?
  694. //
  695. if LUseRtf then
  696. begin
  697. // Add RTF
  698. //
  699. LTextPart := TIdText.Create(AMsg.MessageParts, FRtf);
  700. LTextPart.ContentType := cTextRtf[FRtfType];
  701. LTextPart.ParentPart := LAlternativeIndex; // plain text and optional non-related attachments
  702. end;
  703. end;
  704. procedure TIdMessageBuilderRtf.FillHeaders(AMsg: TIdMessage);
  705. var
  706. LUsePlain, LUseRtf, LUseViewerNeededMsg: Boolean;
  707. begin
  708. if FAttachments.Count = 0 then
  709. begin
  710. LUsePlain := FPlainText.Count > 0;
  711. LUseRtf := FRtf.Count > 0;
  712. LUseViewerNeededMsg := FRtfViewerNeededMsg <> '';
  713. if (LUsePlain) and (not LUseRtf) then
  714. begin
  715. // plain text only
  716. //
  717. AMsg.ContentType := cTextPlain;
  718. AMsg.CharSet := FPlainTextCharSet;
  719. if AMsg.CharSet = '' then begin
  720. AMsg.CharSet := cUTF8;
  721. end;
  722. AMsg.ContentTransferEncoding := FPlainTextContentTransfer;
  723. if AMsg.ContentTransferEncoding = '' then begin
  724. AMsg.ContentTransferEncoding := cQuotedPrintable;
  725. end;
  726. end
  727. else if LUseRtf then
  728. begin
  729. if (not LUsePlain) and (not LUseViewerNeededMsg) then
  730. begin
  731. // RTF only
  732. //
  733. AMsg.ContentType := cTextRtf[FRtfType];
  734. AMsg.CharSet := '';
  735. AMsg.ContentTransferEncoding := '';
  736. end else
  737. begin
  738. // plain text and RTF and no non-related attachments
  739. //
  740. AMsg.ContentType := cMultipartAlternative;
  741. AMsg.CharSet := '';
  742. AMsg.ContentTransferEncoding := '';
  743. end;
  744. end else
  745. begin
  746. // TODO: what to put here?
  747. end;
  748. end else
  749. begin
  750. inherited FillHeaders(AMsg);
  751. end;
  752. end;
  753. procedure TIdMessageBuilderRtf.SetRtf(AValue: TStrings);
  754. begin
  755. FRtf.Assign(AValue);
  756. end;
  757. end.