IdMessageBuilder.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  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. FreeAndNil(Result);
  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. {$IFDEF STRING_IS_UNICODE}
  383. if LTextPart.CharSet = '' then begin
  384. LTextPart.CharSet := cUTF8;
  385. end;
  386. {$ELSE}
  387. // TODO: which default charset to use, if any?
  388. {$ENDIF}
  389. LTextPart.ContentTransfer := FPlainTextContentTransfer;
  390. if LTextPart.ContentTransfer = '' then begin
  391. LTextPart.ContentTransfer := cQuotedPrintable;
  392. end;
  393. end;
  394. end;
  395. end;
  396. procedure TIdMessageBuilderPlain.FillHeaders(AMsg: TIdMessage);
  397. begin
  398. if (FPlainText.Count > 0) and (FAttachments.Count = 0) then
  399. begin
  400. // plain text only
  401. //
  402. AMsg.ContentType := cTextPlain;
  403. AMsg.CharSet := FPlainTextCharSet;
  404. {$IFDEF STRING_IS_UNICODE}
  405. if AMsg.CharSet = '' then begin
  406. AMsg.CharSet := cUTF8;
  407. end;
  408. {$ELSE}
  409. // which default charset to use, if any?
  410. {$ENDIF}
  411. AMsg.ContentTransferEncoding := FPlainTextContentTransfer;
  412. if AMsg.ContentTransferEncoding = '' then begin
  413. AMsg.ContentTransferEncoding := cQuotedPrintable;
  414. end;
  415. end else
  416. begin
  417. inherited FillHeaders(AMsg);
  418. end;
  419. end;
  420. { TIdMessageBuilderHtml }
  421. constructor TIdMessageBuilderHtml.Create;
  422. begin
  423. inherited Create;
  424. FHtml := TStringList.Create;
  425. FHtmlFiles := TIdMessageBuilderAttachments.Create;
  426. FHtmlViewerNeededMsg := rsHtmlViewerNeeded;
  427. end;
  428. destructor TIdMessageBuilderHtml.Destroy;
  429. begin
  430. FHtml.Free;
  431. FHtmlFiles.Free;
  432. inherited Destroy;
  433. end;
  434. procedure TIdMessageBuilderHtml.Clear;
  435. begin
  436. FHtml.Clear;
  437. FHtmlCharSet := '';
  438. FHtmlContentTransfer := '';
  439. FHtmlFiles.Clear;
  440. inherited Clear;
  441. end;
  442. procedure TIdMessageBuilderHtml.FillBody(AMsg: TIdMessage);
  443. var
  444. LUsePlain, LUseHtml, LUseViewerMsg, LUseHtmlFiles, LUseAttachments: Boolean;
  445. LAlternativeIndex, LRelatedIndex: Integer;
  446. LTextPart: TIdText;
  447. begin
  448. // Cache these for better performance
  449. //
  450. LUsePlain := FPlainText.Count > 0;
  451. LUseHtml := FHtml.Count > 0;
  452. LUseViewerMsg := FHtmlViewerNeededMsg <> '';
  453. LUseHtmlFiles := LUseHtml and (FHtmlFiles.Count > 0);
  454. LUseAttachments := FAttachments.Count > 0;
  455. LAlternativeIndex := -1;
  456. LRelatedIndex := -1;
  457. // Is any body data present at all?
  458. //
  459. if not (LUsePlain or LUseHtml or LUseHtmlFiles or LUseAttachments) then begin
  460. Exit;
  461. end;
  462. // Should the message contain only plain text?
  463. //
  464. if LUsePlain and not (LUseHtml or LUseAttachments) then
  465. begin
  466. AMsg.Body.Assign(FPlainText);
  467. Exit;
  468. end;
  469. // Should the message contain only HTML?
  470. //
  471. if LUseHtml and not (LUsePlain or LUseViewerMsg or LUseHtmlFiles or LUseAttachments) then
  472. begin
  473. // TODO: create "multipart/alternative" pieces if FHtmlViewerNeededMsg is not empty...
  474. AMsg.Body.Assign(FHtml);
  475. Exit;
  476. end;
  477. // At this point, multiple pieces will be present in the message
  478. // body, so everything must be stored in the MessageParts collection...
  479. // If the message should contain both plain text and HTML, a
  480. // "multipart/alternative" piece is needed to wrap them if
  481. // non-related attachments are also present...
  482. //
  483. // RLebeau 5/23/2011: need to output the Alternative piece if
  484. // the "HTML Viewer is needed" text is going to be used...
  485. //
  486. if {LUsePlain and} LUseHtml and LUseAttachments then
  487. begin
  488. LTextPart := TIdText.Create(AMsg.MessageParts, nil);
  489. LTextPart.ContentType := cMultipartAlternative;
  490. LAlternativeIndex := LTextPart.Index;
  491. end;
  492. // Is plain text present?
  493. //
  494. if LUsePlain or LUseHtml then
  495. begin
  496. LTextPart := TIdText.Create(AMsg.MessageParts, FPlainText);
  497. if LUseHtml and (not LUsePlain) then begin
  498. LTextPart.Body.Text := FHtmlViewerNeededMsg;
  499. end;
  500. LTextPart.ContentType := cTextPlain;
  501. LTextPart.CharSet := FPlainTextCharSet;
  502. {$IFDEF STRING_IS_UNICODE}
  503. if LTextPart.CharSet = '' then begin
  504. LTextPart.CharSet := cUTF8;
  505. end;
  506. {$ELSE}
  507. // TODO: which default charset to use, if any?
  508. {$ENDIF}
  509. LTextPart.ContentTransfer := FPlainTextContentTransfer;
  510. if LTextPart.ContentTransfer = '' then begin
  511. LTextPart.ContentTransfer := cQuotedPrintable;
  512. end;
  513. LTextPart.ParentPart := LAlternativeIndex;
  514. end;
  515. // Is HTML present?
  516. //
  517. if LUseHtml then
  518. begin
  519. // related attachments can't be referenced by, or used inside
  520. // of, plain text, so there is no point in wrapping the plain
  521. // text inside the same "multipart/related" part with the HTML
  522. // and attachments. Some email programs don't do that as well.
  523. // This logic is newer and more accurate than what is described
  524. // in the "HTML Messages" article found on Indy's website.
  525. //
  526. if LUseHtmlFiles then
  527. begin
  528. LTextPart := TIdText.Create(AMsg.MessageParts, nil);
  529. LTextPart.ContentType := cMultipartRelatedHtml;
  530. LTextPart.ParentPart := LAlternativeIndex;
  531. LRelatedIndex := LTextPart.Index;
  532. end;
  533. // Add HTML
  534. //
  535. LTextPart := TIdText.Create(AMsg.MessageParts, FHtml);
  536. LTextPart.ContentType := cTextHtml;
  537. LTextPart.CharSet := FHtmlCharSet;
  538. {$IFDEF STRING_IS_UNICODE}
  539. if LTextPart.CharSet = '' then begin
  540. LTextPart.CharSet := cUTF8;
  541. end;
  542. {$ELSE}
  543. // TODO: which default charset to use, if any?
  544. {$ENDIF}
  545. LTextPart.ContentTransfer := FHtmlContentTransfer;
  546. if LTextPart.ContentTransfer = '' then begin
  547. LTextPart.ContentTransfer := cQuotedPrintable;
  548. end;
  549. if LRelatedIndex <> -1 then begin
  550. LTextPart.ParentPart := LRelatedIndex; // plain text and related attachments
  551. end else begin
  552. LTextPart.ParentPart := LAlternativeIndex; // plain text and optional non-related attachments
  553. end;
  554. // Are related attachments present?
  555. //
  556. if LUseHtmlFiles then begin
  557. FHtmlFiles.AddToMessage(AMsg, LRelatedIndex);
  558. end;
  559. end;
  560. end;
  561. procedure TIdMessageBuilderHtml.FillHeaders(AMsg: TIdMessage);
  562. var
  563. LUsePlain, LUseHtml, LUseViewerMsg: Boolean;
  564. begin
  565. if FAttachments.Count = 0 then
  566. begin
  567. LUsePlain := FPlainText.Count > 0;
  568. LUseHtml := FHtml.Count > 0;
  569. LUseViewerMsg := FHtmlViewerNeededMsg <> '';
  570. if LUsePlain and (not LUseHtml) then
  571. begin
  572. // plain text only
  573. //
  574. AMsg.ContentType := cTextPlain;
  575. AMsg.CharSet := FPlainTextCharSet;
  576. {$IFDEF STRING_IS_UNICODE}
  577. if AMsg.CharSet = '' then begin
  578. AMsg.CharSet := cUTF8;
  579. end;
  580. {$ELSE}
  581. // TODO: which default charset to use, if any?
  582. {$ENDIF}
  583. AMsg.ContentTransferEncoding := FPlainTextContentTransfer;
  584. if AMsg.ContentTransferEncoding = '' then begin
  585. AMsg.ContentTransferEncoding := cQuotedPrintable;
  586. end;
  587. end
  588. else if LUseHtml then
  589. begin
  590. if (not LUsePlain) and (not LUseViewerMsg) and (FHtmlFiles.Count = 0) then
  591. begin
  592. // HTML only
  593. //
  594. AMsg.ContentType := cTextHtml;
  595. AMsg.CharSet := FHtmlCharSet;
  596. {$IFDEF STRING_IS_UNICODE}
  597. if AMsg.CharSet = '' then begin
  598. AMsg.CharSet := cUTF8;
  599. end;
  600. {$ELSE}
  601. // TODO: which default charset to use, if any?
  602. {$ENDIF}
  603. AMsg.ContentTransferEncoding := FHtmlContentTransfer;
  604. if AMsg.ContentTransferEncoding = '' then begin
  605. AMsg.ContentTransferEncoding := cQuotedPrintable;
  606. end;
  607. end else
  608. begin
  609. // plain text and HTML and no related attachments
  610. //
  611. AMsg.ContentType := cMultipartAlternative;
  612. AMsg.CharSet := '';
  613. AMsg.ContentTransferEncoding := '';
  614. end;
  615. end else
  616. begin
  617. // TODO: what to put here??
  618. end;
  619. end else
  620. begin
  621. inherited FillHeaders(AMsg);
  622. end;
  623. end;
  624. procedure TIdMessageBuilderHtml.SetHtml(AValue: TStrings);
  625. begin
  626. FHtml.Assign(AValue);
  627. end;
  628. procedure TIdMessageBuilderHtml.SetHtmlFiles(AValue: TIdMessageBuilderAttachments);
  629. begin
  630. FHtmlFiles.Assign(AValue);
  631. end;
  632. { TIdMessageBuilderRTF }
  633. constructor TIdMessageBuilderRtf.Create;
  634. begin
  635. inherited Create;
  636. FRtf := TStringList.Create;
  637. FRtfType := idMsgBldrRtfMS;
  638. FRtfViewerNeededMsg := rsRtfViewerNeeded;
  639. end;
  640. destructor TIdMessageBuilderRtf.Destroy;
  641. begin
  642. FRtf.Free;
  643. inherited Destroy;
  644. end;
  645. procedure TIdMessageBuilderRtf.Clear;
  646. begin
  647. FRtf.Clear;
  648. inherited Clear;
  649. end;
  650. procedure TIdMessageBuilderRtf.FillBody(AMsg: TIdMessage);
  651. var
  652. LUsePlain, LUseRtf, LUseViewerMsg, LUseAttachments: Boolean;
  653. LAlternativeIndex: Integer;
  654. LTextPart: TIdText;
  655. begin
  656. // Cache these for better performance
  657. //
  658. LUsePlain := FPlainText.Count > 0;
  659. LUseRtf := FRtf.Count > 0;
  660. LUseViewerMsg := FRtfViewerNeededMsg <> '';
  661. LUseAttachments := FAttachments.Count > 0;
  662. LAlternativeIndex := -1;
  663. // Is any body data present at all?
  664. //
  665. if not (LUsePlain or LUseRtf or LUseAttachments) then begin
  666. Exit;
  667. end;
  668. // Should the message contain only plain text?
  669. //
  670. if LUsePlain and not (LUseRtf or LUseAttachments) then
  671. begin
  672. AMsg.Body.Assign(FPlainText);
  673. Exit;
  674. end;
  675. // Should the message contain only RTF?
  676. //
  677. if LUseRtf and not (LUsePlain or LUseViewerMsg or LUseAttachments) then
  678. begin
  679. // TODO: create "multipart/alternative" pieces if FRtfViewerNeededMsg is not empty...
  680. AMsg.Body.Assign(FRtf);
  681. Exit;
  682. end;
  683. // At this point, multiple pieces will be present in the message
  684. // body, so everything must be stored in the MessageParts collection...
  685. // If the message should contain both plain text and RTF, a
  686. // "multipart/alternative" piece is needed to wrap them if
  687. // attachments are also present...
  688. //
  689. // RLebeau 11/11/2013: need to output the Alternative piece if
  690. // the "RTF Viewer is needed" text is going to be used...
  691. //
  692. if {LUsePlain and} LUseRtf and LUseAttachments then
  693. begin
  694. LTextPart := TIdText.Create(AMsg.MessageParts, nil);
  695. LTextPart.ContentType := cMultipartAlternative;
  696. LAlternativeIndex := LTextPart.Index;
  697. end;
  698. // Is plain text present?
  699. //
  700. if LUsePlain or LUseRtf then
  701. begin
  702. LTextPart := TIdText.Create(AMsg.MessageParts, FPlainText);
  703. if LUseRtf and (not LUsePlain) then begin
  704. LTextPart.Body.Text := FRtfViewerNeededMsg;
  705. end;
  706. LTextPart.ContentType := cTextPlain;
  707. LTextPart.CharSet := FPlainTextCharSet;
  708. {$IFDEF STRING_IS_UNICODE}
  709. if LTextPart.CharSet = '' then begin
  710. LTextPart.CharSet := cUTF8;
  711. end;
  712. {$ELSE}
  713. // TODO: which default charset to use, if any?
  714. {$ENDIF}
  715. LTextPart.ContentTransfer := FPlainTextContentTransfer;
  716. if LTextPart.ContentTransfer = '' then begin
  717. LTextPart.ContentTransfer := cQuotedPrintable;
  718. end;
  719. LTextPart.ParentPart := LAlternativeIndex;
  720. end;
  721. // Is RTF present?
  722. //
  723. if LUseRtf then
  724. begin
  725. // Add RTF
  726. //
  727. LTextPart := TIdText.Create(AMsg.MessageParts, FRtf);
  728. LTextPart.ContentType := cTextRtf[FRtfType];
  729. LTextPart.ParentPart := LAlternativeIndex; // plain text and optional non-related attachments
  730. end;
  731. end;
  732. procedure TIdMessageBuilderRtf.FillHeaders(AMsg: TIdMessage);
  733. var
  734. LUsePlain, LUseRtf, LUseViewerMsg: Boolean;
  735. begin
  736. if FAttachments.Count = 0 then
  737. begin
  738. LUsePlain := FPlainText.Count > 0;
  739. LUseRtf := FRtf.Count > 0;
  740. LUseViewerMsg := FRtfViewerNeededMsg <> '';
  741. if (LUsePlain) and (not LUseRtf) then
  742. begin
  743. // plain text only
  744. //
  745. AMsg.ContentType := cTextPlain;
  746. AMsg.CharSet := FPlainTextCharSet;
  747. {$IFDEF STRING_IS_UNICODE}
  748. if AMsg.CharSet = '' then begin
  749. AMsg.CharSet := cUTF8;
  750. end;
  751. {$ELSE}
  752. // TODO: which default charset to use, if any?
  753. {$ENDIF}
  754. AMsg.ContentTransferEncoding := FPlainTextContentTransfer;
  755. if AMsg.ContentTransferEncoding = '' then begin
  756. AMsg.ContentTransferEncoding := cQuotedPrintable;
  757. end;
  758. end
  759. else if LUseRtf then
  760. begin
  761. if (not LUsePlain) and (not LUseViewerMsg) then
  762. begin
  763. // RTF only
  764. //
  765. AMsg.ContentType := cTextRtf[FRtfType];
  766. AMsg.CharSet := '';
  767. AMsg.ContentTransferEncoding := '';
  768. end else
  769. begin
  770. // plain text and RTF and no non-related attachments
  771. //
  772. AMsg.ContentType := cMultipartAlternative;
  773. AMsg.CharSet := '';
  774. AMsg.ContentTransferEncoding := '';
  775. end;
  776. end else
  777. begin
  778. // TODO: what to put here?
  779. end;
  780. end else
  781. begin
  782. inherited FillHeaders(AMsg);
  783. end;
  784. end;
  785. procedure TIdMessageBuilderRtf.SetRtf(AValue: TStrings);
  786. begin
  787. FRtf.Assign(AValue);
  788. end;
  789. end.