IdMessageClient.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10253: IdMessageClient.pas
  11. {
  12. { Rev 1.13 7/23/04 6:11:26 PM RLebeau
  13. { TFileStream access right tweak for ProcessMessage()
  14. }
  15. {
  16. { Rev 1.12 5/12/04 9:52:06 AM RLebeau
  17. { Updated ProcessMessage() to call ReceiveBody() only if ReceiveHeader() does
  18. { not receive the message terminator first
  19. }
  20. {
  21. { Rev 1.11 5/2/04 7:58:08 PM RLebeau
  22. { Updated TIdIOHandlerStreamMsg.Recv() to not use a local buffer anymore
  23. }
  24. {
  25. { Rev 1.10 5/1/04 3:04:16 AM RLebeau
  26. { Bug fix for TIdIOHandlerStreamMsg, and also updated to keep track of the last
  27. { character received from the stream so that extra CR LF characters are not
  28. { added to the end of the message data unnecessarily.
  29. }
  30. {
  31. { Rev 1.9 4/23/04 1:54:22 PM RLebeau
  32. { Added support for TIdIOHandlerStreamMsg class
  33. }
  34. {
  35. { Rev 1.8 2/3/04 11:59:20 AM RLebeau
  36. { Updated SendBody() to output the TIdMessagePart.ContentID property if it is
  37. { assigned.
  38. }
  39. {
  40. { Rev 1.7 10/17/03 11:50:46 AM RLebeau
  41. { Updated ReceiveBody() to copy all available header values from the message
  42. { decoder when creating TIdText and TIdAttachment instances rather than just
  43. { select values.
  44. }
  45. {
  46. { Rev 1.6 2003.07.03 11:52:08 AM czhower
  47. { DeleteTempFiles addition.
  48. { Fix of old property IsTempFile, changed to DeleteTempFile so as not to change
  49. { broken but old functionality that could otherwise cause data loss.
  50. }
  51. {
  52. { Rev 1.5 2003.06.15 3:00:34 PM czhower
  53. { -Fixed IdIOHandlerStream to function as originally designed and needed.
  54. { -Change ReadStream, WriteStream to Input/Output to be consistent with other
  55. { areas.
  56. }
  57. {
  58. { Rev 1.4 21/2/2003 1:53:10 PM SGrobety
  59. { Fixed a problem when the message contained only a single text part
  60. }
  61. {
  62. { Rev 1.3 11-30-2002 11:49:50 BGooijen
  63. { Fixed double if keywork in if-statement, which caused to file not to compile
  64. }
  65. {
  66. { Rev 1.2 11/23/2002 03:23:08 AM JPMugaas
  67. { Reverted back to old way because the fix turned out to be problematic.
  68. }
  69. {
  70. { Rev 1.1 11/19/2002 05:24:10 PM JPMugaas
  71. { Fixed problem with a . starting a line causing a duplicate period where it
  72. { shouldn't.
  73. }
  74. {
  75. { Rev 1.0 2002.11.12 10:45:48 PM czhower
  76. }
  77. unit IdMessageClient;
  78. {
  79. 2001-Oct-29 Don Siders
  80. Modified TIdMessageClient.SendMsg to use AHeadersOnly argument.
  81. 2001-Dec-1 Don Siders
  82. Save ContentDisposition in TIdMessageClient.ProcessAttachment
  83. }
  84. interface
  85. uses
  86. Classes,
  87. IdGlobal, IdIOHandlerStream, IdMessage, IdTCPClient, IdHeaderList;
  88. type
  89. TIdIOHandlerStreamMsg = class(TIdIOHandlerStream)
  90. protected
  91. FTerminator: String;
  92. FTerminatorIndex: Integer;
  93. FLastCharRecv: Char;
  94. public
  95. constructor Create(AOwner: TComponent); override;
  96. function Readable(AMSec: integer = IdTimeoutDefault): boolean; override;
  97. function Recv(var ABuf; ALen: integer): integer; override;
  98. end;
  99. TIdMessageClient = class(TIdTCPClient)
  100. protected
  101. // The length of the folded line
  102. FMsgLineLength: integer;
  103. // The string to be pre-pended to the next line
  104. FMsgLineFold: string;
  105. //
  106. procedure ReceiveBody(AMsg: TIdMessage; const ADelim: string = '.'); virtual;
  107. function ReceiveHeader(AMsg: TIdMessage; const AAltTerm: string = ''): string; virtual;
  108. procedure SendBody(AMsg: TIdMessage); virtual;
  109. procedure SendHeader(AMsg: TIdMessage); virtual;
  110. procedure WriteBodyText(AMsg: TIdMessage); virtual;
  111. procedure WriteFoldedLine(const ALine : string);
  112. public
  113. constructor Create(AOwner : TComponent); override;
  114. procedure ProcessMessage(AMsg: TIdMessage; AHeaderOnly: Boolean = False); overload;
  115. procedure ProcessMessage(AMsg: TIdMessage; const AStream: TStream; AHeaderOnly: Boolean = False); overload;
  116. procedure ProcessMessage(AMsg: TIdMessage; const AFilename: string; AHeaderOnly: Boolean = False); overload;
  117. procedure SendMsg(AMsg: TIdMessage; const AHeadersOnly: Boolean = False); virtual;
  118. //
  119. property MsgLineLength: integer read FMsgLineLength write FMsgLineLength;
  120. property MsgLineFold: string read FMsgLineFold write FMsgLineFold;
  121. end;
  122. implementation
  123. uses
  124. //TODO: Remove these references and make it completely pluggable. Check other spots in Indy as well
  125. IdCoderQuotedPrintable, IdMessageCoderMIME, IdMessageCoderUUE, IdMessageCoderXXE,
  126. //
  127. IdCoder, IdCoder3to4,
  128. IdCoderHeader, IdMessageCoder, IdComponent, IdException, IdResourceStrings, IdTCPConnection,
  129. IdTCPStream, IdIOHandler,
  130. SysUtils;
  131. const
  132. SMsgTerminator = #13#10'.'#13#10; {do not localize}
  133. function GetLongestLine(var ALine : String; ADelim : String) : String;
  134. var
  135. i, fnd, lineLen, delimLen : Integer;
  136. begin
  137. i := 0;
  138. fnd := -1;
  139. delimLen := length(ADelim);
  140. lineLen := length(ALine);
  141. while i < lineLen do
  142. begin
  143. if ALine[i] = ADelim[1] then
  144. begin
  145. if Copy(ALine, i, delimLen) = ADelim then
  146. begin
  147. fnd := i;
  148. end;
  149. end;
  150. Inc(i);
  151. end;
  152. if fnd = -1 then
  153. begin
  154. result := '';
  155. end
  156. else begin
  157. result := Copy(ALine, 1, fnd - 1);
  158. ALine := Copy(ALine, fnd + delimLen, lineLen);
  159. end;
  160. end;
  161. ////////////////////////
  162. // TIdIOHandlerStreamMsg
  163. ////////////////////////
  164. constructor TIdIOHandlerStreamMsg.Create(AOwner: TComponent);
  165. begin
  166. inherited Create(AOwner);
  167. FTerminator := SMsgTerminator;
  168. FTerminatorIndex := 0;
  169. FLastCharRecv := #0;
  170. end;
  171. function TIdIOHandlerStreamMsg.Readable(AMSec: integer = IdTimeoutDefault): boolean;
  172. begin
  173. // if the terminator is not started yet, check the source data first...
  174. if FTerminatorIndex = 0 then begin
  175. Result := inherited Readable(AMSec);
  176. if Result then begin
  177. Exit;
  178. end;
  179. end;
  180. // check the terminator next...
  181. if InputStream <> nil then begin
  182. Result := (FTerminatorIndex <= Length(FTerminator));
  183. end else begin
  184. Result := False
  185. end;
  186. end;
  187. function TIdIOHandlerStreamMsg.Recv(var ABuf; ALen: integer): integer;
  188. begin
  189. // if the terminator is not started yet, check the source data first...
  190. if FTerminatorIndex = 0 then begin
  191. Result := inherited Recv(ABuf, ALen);
  192. if Result > 0 then begin
  193. // save the last character received for later use, see below
  194. FLastCharRecv := PChar(@ABuf)[Result-1];
  195. Exit;
  196. end;
  197. if (ALen <= 0) then begin
  198. // buffer size not specified, just return now without starting the terminator yet...
  199. Exit;
  200. end;
  201. // determine whether the stream ended with a line
  202. // break, adding an extra CR and/or LF if needed...
  203. if (FLastCharRecv = LF) then begin
  204. // don't add an extra line break
  205. FTerminatorIndex := 3;
  206. end else if (FLastCharRecv = CR) then begin
  207. // add extra LF
  208. FTerminatorIndex := 2;
  209. end else begin
  210. // add extra CRLF
  211. FTerminatorIndex := 1;
  212. end;
  213. end;
  214. // return the appropriate piece of the terminator...
  215. ALen := Min(ALen, (Length(FTerminator)-FTerminatorIndex)+1);
  216. if ALen > 0 then begin
  217. Move(FTerminator[FTerminatorIndex], ABuf, ALen);
  218. Inc(FTerminatorIndex, ALen);
  219. end;
  220. Result := ALen;
  221. end;
  222. ///////////////////
  223. // TIdMessageClient
  224. ///////////////////
  225. constructor TIdMessageClient.Create;
  226. begin
  227. inherited;
  228. FMsgLineLength := 79;
  229. FMsgLineFold := TAB;
  230. end;
  231. procedure TIdMessageClient.WriteFoldedLine;
  232. var
  233. ins, s, line, spare : String;
  234. msgLen, insLen : Word;
  235. begin
  236. s := ALine;
  237. // To give an amount of thread-safety
  238. ins := FMsgLineFold;
  239. insLen := Length(ins);
  240. msgLen := FMsgLineLength;
  241. // Do first line
  242. if length(s) > FMsgLineLength then
  243. begin
  244. spare := Copy(s, 1, msgLen);
  245. line := GetLongestLine(spare, ' ');
  246. s := spare + Copy(s, msgLen + 1, length(s));
  247. WriteLn(line);
  248. // continue with the folded lines
  249. while length(s) > (msgLen - insLen) do
  250. begin
  251. spare := Copy(s, 1, (msgLen - insLen));
  252. line := GetLongestLine(spare, ' ');
  253. s := ins + spare + Copy(s, (msgLen - insLen) + 1, length(s));
  254. WriteLn(line);
  255. end;
  256. // complete the output with what's left
  257. if Trim(s) <> '' then
  258. begin
  259. WriteLn(ins + s);
  260. end;
  261. end
  262. else begin
  263. WriteLn(s);
  264. end;
  265. end;
  266. procedure TIdMessageClient.ReceiveBody(AMsg: TIdMessage; const ADelim: string = '.');
  267. var
  268. LMsgEnd: Boolean;
  269. LActiveDecoder: TIdMessageDecoder;
  270. LLine: string;
  271. function ProcessTextPart(ADecoder: TIdMessageDecoder): TIdMessageDecoder;
  272. var
  273. LDestStream: TStringStream;
  274. begin
  275. LDestStream := TStringStream.Create('');
  276. try
  277. Result := ADecoder.ReadBody(LDestStream, LMsgEnd);
  278. with TIdText.Create(AMsg.MessageParts) do
  279. begin
  280. {
  281. ContentType := ADecoder.Headers.Values['Content-Type'];
  282. ContentTransfer := ADecoder.Headers.Values['Content-Transfer-Encoding'];
  283. }
  284. // RLebeau 10/17/2003
  285. Headers.AddStdValues(ADecoder.Headers);
  286. Body.Text := LDestStream.DataString;
  287. end;
  288. ADecoder.Free;
  289. finally
  290. FreeAndNil(LDestStream);
  291. end;
  292. end;
  293. function ProcessAttachment(ADecoder: TIdMessageDecoder): TIdMessageDecoder;
  294. var
  295. LDestStream: TFileStream;
  296. LTempPathname: string;
  297. begin
  298. LTempPathname := MakeTempFilename;
  299. LDestStream := TFileStream.Create(LTempPathname, fmCreate);
  300. try
  301. Result := ADecoder.ReadBody(LDestStream, LMsgEnd);
  302. with TIdAttachment.Create(AMsg.MessageParts) do
  303. begin
  304. DeleteTempFile := AMsg.DeleteTempFiles;
  305. {
  306. ContentType := ADecoder.Headers.Values['Content-Type'];
  307. ContentTransfer := ADecoder.Headers.Values['Content-Transfer-Encoding'];
  308. // dsiders 2001.12.01
  309. ContentDisposition := ADecoder.Headers.Values['Content-Disposition'];
  310. }
  311. // RLebeau 10/17/2003
  312. Headers.AddStdValues(ADecoder.Headers);
  313. Filename := ADecoder.Filename;
  314. StoredPathname := LTempPathname;
  315. end;
  316. ADecoder.Free;
  317. finally
  318. FreeAndNil(LDestStream);
  319. end;
  320. end;
  321. const
  322. wDoublePoint = ord('.') shl 8 + ord('.');
  323. Begin
  324. LMsgEnd := False;
  325. if AMsg.NoDecode then
  326. begin
  327. Capture(AMsg.Body, ADelim);
  328. end
  329. else begin
  330. BeginWork(wmRead);
  331. try
  332. LActiveDecoder := nil;
  333. repeat
  334. LLine := ReadLn;
  335. if LLine = ADelim then
  336. begin
  337. Break;
  338. end;
  339. if LActiveDecoder = nil then
  340. begin
  341. LActiveDecoder := TIdMessageDecoderList.CheckForStart(AMsg, LLine);
  342. end;
  343. if LActiveDecoder = nil then begin
  344. if PWord(PChar(LLine))^= wDoublePoint then begin
  345. Delete(LLine,1,1);
  346. end;//if '..'
  347. AMsg.Body.Add(LLine);
  348. end else begin
  349. while LActiveDecoder <> nil do begin
  350. LActiveDecoder.SourceStream := TIdTCPStream.Create(Self);
  351. LActiveDecoder.ReadHeader;
  352. case LActiveDecoder.PartType of
  353. mcptUnknown:
  354. begin
  355. raise EIdException.Create(RSMsgClientUnkownMessagePartType);
  356. end;
  357. mcptText:
  358. begin
  359. LActiveDecoder := ProcessTextPart(LActiveDecoder);
  360. end;
  361. mcptAttachment:
  362. begin
  363. LActiveDecoder := ProcessAttachment(LActiveDecoder);
  364. end;
  365. end;
  366. end;
  367. end;
  368. until LMsgEnd;
  369. finally
  370. EndWork(wmRead);
  371. end;
  372. end;
  373. end;
  374. procedure TIdMessageClient.SendHeader(AMsg: TIdMessage);
  375. var
  376. LHeaders: TIdHeaderList;
  377. begin
  378. LHeaders := AMsg.GenerateHeader;
  379. try
  380. WriteStrings(LHeaders);
  381. finally
  382. FreeAndNil(LHeaders);
  383. end;
  384. end;
  385. procedure TIdMessageClient.SendBody(AMsg: TIdMEssage);
  386. var
  387. i: Integer;
  388. LAttachment: TIdAttachment;
  389. LBoundary: string;
  390. LDestStream: TIdTCPStream;
  391. LMIMEAttachments: boolean;
  392. ISOCharset: string;
  393. HeaderEncoding: Char; { B | Q }
  394. TransferEncoding: TTransfer;
  395. procedure WriteTextPart(ATextPart: TIdText);
  396. var
  397. Data: string;
  398. i: Integer;
  399. begin
  400. if Length(ATextPart.ContentType) = 0 then
  401. ATextPart.ContentType := 'text/plain'; {do not localize}
  402. if Length(ATextPart.ContentTransfer) = 0 then
  403. ATextPart.ContentTransfer := 'quoted-printable'; {do not localize}
  404. WriteLn('Content-Type: ' + ATextPart.ContentType); {do not localize}
  405. WriteLn('Content-Transfer-Encoding: ' + ATextPart.ContentTransfer); {do not localize}
  406. if Length(ATextPart.ContentID) <> 0 then
  407. begin
  408. WriteLn('Content-Id: ' + ATextPart.ContentID);
  409. end;
  410. WriteStrings(ATextPart.ExtraHeaders);
  411. WriteLn('');
  412. // TODO: Provide B64 encoding later
  413. // if AnsiSameText(ATextPart.ContentTransfer, 'base64') then begin
  414. // LEncoder := TIdEncoder3to4.Create(nil);
  415. if AnsiSameText(ATextPart.ContentTransfer, 'quoted-printable') then
  416. begin
  417. for i := 0 to ATextPart.Body.Count - 1 do
  418. begin
  419. if Copy(ATextPart.Body[i], 1, 1) = '.' then
  420. begin
  421. ATextPart.Body[i] := '.' + ATextPart.Body[i];
  422. end;
  423. Data := TIdEncoderQuotedPrintable.EncodeString(ATextPart.Body[i] + EOL);
  424. if TransferEncoding = iso2022jp then
  425. Write(Encode2022JP(Data))
  426. else
  427. Write(Data);
  428. end;
  429. end
  430. else begin
  431. WriteStrings(ATextPart.Body);
  432. end;
  433. WriteLn('');
  434. end;
  435. begin
  436. LMIMEAttachments := AMsg.Encoding = meMIME;
  437. LBoundary := '';
  438. InitializeISO(TransferEncoding, HeaderEncoding, ISOCharSet);
  439. BeginWork(wmWrite);
  440. try
  441. if AMsg.MessageParts.AttachmentCount > 0 then
  442. begin
  443. if LMIMEAttachments then
  444. begin
  445. WriteLn('This is a multi-part message in MIME format'); {do not localize}
  446. WriteLn('');
  447. if AMsg.MessageParts.RelatedPartCount > 0 then
  448. begin
  449. LBoundary := IndyMultiPartRelatedBoundary;
  450. end
  451. else begin
  452. LBoundary := IndyMIMEBoundary;
  453. end;
  454. WriteLn('--' + LBoundary);
  455. end
  456. else begin
  457. // It's UU, write the body
  458. WriteBodyText(AMsg);
  459. WriteLn('');
  460. end;
  461. if AMsg.MessageParts.TextPartCount > 1 then
  462. begin
  463. WriteLn('Content-Type: multipart/alternative; '); {do not localize}
  464. WriteLn(' boundary="' + IndyMultiPartAlternativeBoundary + '"'); {do not localize}
  465. WriteLn('');
  466. for i := 0 to AMsg.MessageParts.Count - 1 do
  467. begin
  468. if AMsg.MessageParts.Items[i] is TIdText then
  469. begin
  470. WriteLn('--' + IndyMultiPartAlternativeBoundary);
  471. DoStatus(hsStatusText, [RSMsgClientEncodingText]);
  472. WriteTextPart(AMsg.MessageParts.Items[i] as TIdText);
  473. WriteLn('');
  474. end;
  475. end;
  476. WriteLn('--' + IndyMultiPartAlternativeBoundary + '--');
  477. end
  478. // RLebeau 2/3/2006: If the user added a single texpart message without filling the body
  479. // RLebeau 2/3/2003: we still need to send that out
  480. else if AMsg.MessageParts.TextPartCount = 1 then
  481. begin
  482. for i := 0 to AMsg.MessageParts.Count - 1 do
  483. begin
  484. if AMsg.MessageParts.Items[i] is TIdText then
  485. begin
  486. DoStatus(hsStatusText, [RSMsgClientEncodingText]);
  487. WriteTextPart(AMsg.MessageParts.Items[i] as TIdText);
  488. Break;
  489. end;
  490. end;
  491. end
  492. else begin
  493. if LMIMEAttachments then
  494. begin
  495. WriteLn('Content-Type: text/plain'); {do not localize}
  496. WriteLn('Content-Transfer-Encoding: 7bit'); {do not localize}
  497. WriteLn('');
  498. WriteBodyText(AMsg);
  499. end;
  500. end;
  501. // Send the attachments
  502. for i := 0 to AMsg.MessageParts.Count - 1 do
  503. begin
  504. if AMsg.MessageParts[i] is TIdAttachment then
  505. begin
  506. LAttachment := TIdAttachment(AMsg.MessageParts[i]);
  507. DoStatus(hsStatusText, [RSMsgClientEncodingAttachment]);
  508. if LMIMEAttachments then
  509. begin
  510. WriteLn('');
  511. WriteLn('--' + LBoundary);
  512. if Length(LAttachment.ContentTransfer) = 0 then
  513. begin
  514. LAttachment.ContentTransfer := 'base64'; {do not localize}
  515. end;
  516. if Length(LAttachment.ContentDisposition) = 0 then
  517. begin
  518. LAttachment.ContentDisposition := 'attachment'; {do not localize}
  519. end;
  520. if (LAttachment.ContentTransfer = 'base64') {do not localize}
  521. and (Length(LAttachment.ContentType) = 0) then
  522. begin
  523. LAttachment.ContentType := 'application/octet-stream'; {do not localize}
  524. end;
  525. WriteLn('Content-Type: ' + LAttachment.ContentType + ';'); {do not localize}
  526. WriteLn(' name="' + ExtractFileName(LAttachment.FileName) + '"'); {do not localize}
  527. WriteLn('Content-Transfer-Encoding: ' + LAttachment.ContentTransfer); {do not localize}
  528. WriteLn('Content-Disposition: ' + LAttachment.ContentDisposition +';'); {do not localize}
  529. WriteLn(' filename="' + ExtractFileName(LAttachment.FileName) + '"'); {do not localize}
  530. if Length(LAttachment.ContentID) <> 0 then
  531. begin
  532. WriteLn('Content-Id: ' + LAttachment.ContentID);
  533. end;
  534. WriteStrings(LAttachment.ExtraHeaders);
  535. WriteLn('');
  536. end;
  537. LDestStream := TIdTCPStream.Create(Self, 8192);
  538. try
  539. TIdAttachment(AMsg.MessageParts[i]).Encode(LDestStream);
  540. finally
  541. FreeAndNil(LDestStream);
  542. end;
  543. WriteLn('');
  544. end;
  545. end;
  546. if LMIMEAttachments then
  547. begin
  548. WriteLn('--' + LBoundary + '--');
  549. end;
  550. end
  551. // S.G. 21/2/2003: If the user added a single texpart message without filling the body
  552. // S.G. 21/2/2003: we still need to send that out
  553. else
  554. if (AMsg.MessageParts.TextPartCount > 1) or
  555. ((AMsg.MessageParts.TextPartCount = 1) and (AMsg.Body.Count = 0)) then
  556. begin
  557. WriteLn('This is a multi-part message in MIME format'); {do not localize}
  558. WriteLn('');
  559. for i := 0 to AMsg.MessageParts.Count - 1 do
  560. begin
  561. if AMsg.MessageParts.Items[i] is TIdText then
  562. begin
  563. WriteLn('--' + IndyMIMEBoundary);
  564. DoStatus(hsStatusText, [RSMsgClientEncodingText]);
  565. WriteTextPart(AMsg.MessageParts.Items[i] as TIdText);
  566. end;
  567. end;
  568. WriteLn('--' + IndyMIMEBoundary + '--');
  569. end
  570. else begin
  571. DoStatus(hsStatusText, [RSMsgClientEncodingText]);
  572. // Write out Body
  573. //TODO: Why just iso2022jp? Why not someting generic for all MBCS? Or is iso2022jp special?
  574. if TransferEncoding = iso2022jp then
  575. begin
  576. for i := 0 to AMsg.Body.Count - 1 do
  577. begin
  578. if Copy(AMsg.Body[i], 1, 1) = '.' then
  579. begin
  580. WriteLn('.' + Encode2022JP(AMsg.Body[i]));
  581. end
  582. else begin
  583. WriteLn(Encode2022JP(AMsg.Body[i]));
  584. end;
  585. end;
  586. end
  587. else begin
  588. WriteBodyText(AMsg);
  589. end;
  590. end;
  591. finally
  592. EndWork(wmWrite);
  593. end;
  594. end;
  595. { 2001-Oct-29 Don Siders
  596. procedure TIdMessageClient.SendMsg(AMsg: TIdMessage);
  597. begin
  598. SendHeader(AMsg);
  599. WriteLn('');
  600. SendBody(AMsg);
  601. end; }
  602. // 2001-Oct-29 Don Siders Added AHeadersOnly parameter
  603. // TODO: Override TIdMessageClient.SendMsg to provide socket, stream, and file
  604. // versions like TIdMessageClient.ProcessMessage?
  605. procedure TIdMessageClient.SendMsg(AMsg: TIdMessage; const AHeadersOnly: Boolean = False);
  606. begin
  607. if AMsg.NoEncode then begin
  608. WriteStringS(AMsg.Headers);
  609. WriteLn('');
  610. if not AHeadersOnly then begin
  611. WriteStrings(AMsg.Body);
  612. end;
  613. end else begin
  614. SendHeader(AMsg);
  615. WriteLn('');
  616. if (not AHeadersOnly) then SendBody(AMsg);
  617. end;
  618. end;
  619. function TIdMessageClient.ReceiveHeader(AMsg: TIdMessage; const AAltTerm: string = ''): string;
  620. begin
  621. BeginWork(wmRead); try
  622. repeat
  623. Result := ReadLn;
  624. // Exchange Bug: Exchange sometimes returns . when getting a message instead of
  625. // '' then a . - That is there is no seperation between the header and the message for an
  626. // empty message.
  627. if ((Length(AAltTerm) = 0) and (Result = '.')) or
  628. ({APR: why? (Length(AAltTerm) > 0) and }(Result = AAltTerm)) then begin
  629. Break;
  630. end else if Result <> '' then begin
  631. AMsg.Headers.Append(Result);
  632. end;
  633. until False;
  634. AMsg.ProcessHeaders;
  635. finally EndWork(wmRead); end;
  636. end;
  637. procedure TIdMessageClient.ProcessMessage(AMsg: TIdMessage; AHeaderOnly: Boolean = False);
  638. begin
  639. if IOHandler <> nil then
  640. begin
  641. if Length(ReceiveHeader(AMsg)) = 0 then begin
  642. // Only retreive the body if we do not already have a full RFC
  643. if (not AHeaderOnly) then begin
  644. ReceiveBody(AMsg);
  645. end;
  646. end;
  647. end;
  648. end;
  649. procedure TIdMessageClient.ProcessMessage(AMsg: TIdMessage; const AStream: TStream; AHeaderOnly: Boolean = False);
  650. var
  651. LIOHSM: TIdIOHandlerStreamMsg;
  652. begin
  653. LIOHSM := TIdIOHandlerStreamMsg.Create(nil); try
  654. LIOHSM.InputStream := AStream;
  655. LIOHSM.FreeStreams := False;
  656. IOHandler := LIOHSM; try
  657. Connect; try
  658. ProcessMessage(AMsg, AHeaderOnly);
  659. finally Disconnect; end;
  660. finally IOHandler := nil; end;
  661. finally FreeAndNil(LIOHSM); end;
  662. end;
  663. procedure TIdMessageClient.ProcessMessage(AMsg: TIdMessage; const AFilename: string; AHeaderOnly: Boolean = False);
  664. var
  665. LStream: TFileStream;
  666. begin
  667. LStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
  668. try
  669. ProcessMessage(AMsg, LStream, AHeaderOnly);
  670. finally
  671. FreeAndNil(LStream);
  672. end;
  673. end;
  674. procedure TIdMessageClient.WriteBodyText(AMsg: TIdMessage);
  675. var
  676. i: integer;
  677. begin
  678. for i := 0 to AMsg.Body.Count - 1 do
  679. begin
  680. if Copy(AMsg.Body[i], 1, 1) = '.' then
  681. begin
  682. WriteLn('.' + AMsg.Body[i]);
  683. end
  684. else begin
  685. WriteLn(AMsg.Body[i]);
  686. end;
  687. end;
  688. end;
  689. end.