IdCoderQuotedPrintable.pas 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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: 10097: IdCoderQuotedPrintable.pas
  11. {
  12. { Rev 1.1 12.6.2003 ã. 11:46:58 DBondzhev
  13. { Fix for '.' when it is going to be alone on the next line. This breaks the
  14. { message parts and messagess are truncated or incorrectly recived.
  15. }
  16. {
  17. { Rev 1.0 2002.11.12 10:32:46 PM czhower
  18. }
  19. unit IdCoderQuotedPrintable;
  20. {9-17-2001 - J. Peter Mugaas
  21. made the interpretation of =20 + EOL to mean a hard line break
  22. soft line breaks are now ignored. It does not make much sense
  23. in plain text. Soft breaks do not indicate the end of paragraphs unlike
  24. hard line breaks that do end paragraphs.
  25. 3-24-2001 - J. Peter Mugaas
  26. Rewrote the Decoder according to a new design.
  27. 3-25-2001 - J. Peter Mugaas
  28. Rewrote the Encoder according to the new design}
  29. interface
  30. uses
  31. Classes,
  32. IdCoder;
  33. type
  34. TIdDecoderQuotedPrintable = class(TIdDecoder)
  35. public
  36. procedure DecodeToStream(AIn: string; ADest: TStream); override;
  37. end;
  38. TIdEncoderQuotedPrintable = class(TIdEncoder)
  39. public
  40. function Encode(ASrcStream: TStream; const ABytes: integer = MaxInt): string; override;
  41. end;
  42. implementation
  43. uses
  44. IdGlobal,
  45. SysUtils;
  46. { TIdDecoderQuotedPrintable }
  47. procedure TIdDecoderQuotedPrintable.DecodeToStream(AIn: string; ADest: TStream);
  48. var
  49. Buffer, Line, Hex : String;
  50. i : Integer;
  51. b : Byte;
  52. const
  53. Numbers = '01234567890ABCDEF'; {Do not Localize}
  54. procedure StripEOLChars;
  55. var j : Integer;
  56. begin
  57. for j := 1 to 2 do
  58. begin
  59. if (Length(Buffer) > 0) and
  60. (Pos(Buffer[1],EOL) > 0) then
  61. begin
  62. Delete(Buffer,1,1);
  63. end
  64. else
  65. begin
  66. break;
  67. end;
  68. end;
  69. end;
  70. function TrimRightWhiteSpace(const Str : String) : String;
  71. var
  72. i : integer;
  73. LSaveStr : String;
  74. begin
  75. SetLength(LSaveStr,0);
  76. i := Length(Str);
  77. while (i > 0) and (Str[i] in [#9,#32]+[#10,#13]) do
  78. begin
  79. if Str[i] in [#10,#13] then
  80. begin
  81. Insert(Str[i],LSaveStr,1);
  82. end;
  83. dec(i);
  84. end;
  85. result := Copy(Str,1,i) + LSaveStr;
  86. end;
  87. begin
  88. Line := ''; {Do not Localize}
  89. { when decoding a Quoted-Printable body, any trailing
  90. white space on a line must be deleted, - RFC 1521}
  91. Buffer := TrimRightWhiteSpace(AIn);
  92. while Length(Buffer) > 0 do
  93. begin
  94. Line := Line + Fetch(Buffer,'='); {Do not Localize}
  95. // process any following hexidecimal represntation
  96. if Length(Buffer) > 0 then
  97. begin
  98. Hex := ''; {Do not Localize}
  99. for i := 0 to 1 do
  100. begin
  101. If IndyPos(UpperCase(Buffer[1]),Numbers) <> 0 then
  102. begin
  103. Hex := Hex + Copy(Buffer,1,1);
  104. Delete(Buffer,1,1);
  105. end
  106. else
  107. begin
  108. break;
  109. end;
  110. end;
  111. if (Length(Hex) > 0) then
  112. begin
  113. b := StrToInt('$'+Hex); {Do not Localize}
  114. //if =20 + EOL, this is a hard line break after a space
  115. if (b = 32) and
  116. (Length(Buffer) > 0) and
  117. (Pos(Buffer[1],EOL) > 0) then
  118. begin
  119. Line := Line + Char(b) + EOL;
  120. StripEOLChars;
  121. end
  122. else
  123. begin
  124. Line := Line + Char(b);
  125. end;
  126. end
  127. else
  128. begin
  129. //ignore soft line breaks -
  130. StripEOLChars;
  131. end;
  132. end;
  133. end;
  134. if Length(Line) > 0 then
  135. begin
  136. ADest.Write(Line[1],Length(Line));
  137. end;
  138. end;
  139. { TIdEncoderQuotedPrintable }
  140. function TIdEncoderQuotedPrintable.Encode(ASrcStream: TStream; const ABytes: integer): string;
  141. //TODO: Change this to be more efficient - dont read the whole data in ahead of time as it may
  142. // be quite large
  143. const BUF_SIZE = 8192;
  144. var
  145. i, LDataSize, LBytesRead, LBufSize : Integer;
  146. Buffer : Array [1..BUF_SIZE] of char;
  147. Line : String;
  148. st : TStrings;
  149. s : String;
  150. Procedure NewLine;
  151. begin
  152. Line := Line + '='; {Do not Localize}
  153. st.Add(Line);
  154. Line := ''; {Do not Localize}
  155. end;
  156. Function QPHex(c : Char) : String;
  157. begin
  158. Result := '='+ IntToHex(Ord(c),2); {Do not Localize}
  159. end;
  160. begin
  161. st := TStringList.Create;
  162. try
  163. Result := ''; {Do not Localize}
  164. Line := ''; {Do not Localize}
  165. LBytesRead := 0;
  166. LDataSize := ASrcStream.Size - ASrcStream.Position;
  167. if LDataSize > ABytes then
  168. begin
  169. LDataSize := ABytes;
  170. end;
  171. if (LDataSize > 0) then
  172. begin
  173. while LBytesRead < LDataSize do
  174. begin
  175. if (LDataSize - LBytesRead) > BUF_SIZE then
  176. begin
  177. LBufSize := BUF_SIZE
  178. end
  179. else
  180. begin
  181. LBufSize := LDataSize - LBytesRead;
  182. end;
  183. ASrcStream.Read(Buffer[1],LBufSize);
  184. LBytesRead := LBytesRead + LBufSize;
  185. For i := 1 to LBufSize do
  186. begin
  187. case Buffer[i] of
  188. // Special case when '.' is about to be alone on the next line.
  189. '.': begin
  190. if Line = '' then begin
  191. s := QPHex(Buffer[i]);
  192. Line := Line + s;
  193. end else begin
  194. Line := Line + Buffer[i];
  195. end;
  196. end;
  197. ' ', TAB: {Do not Localize}
  198. If (i < Length(Buffer)) and (Buffer[i+1] in [#10,#13]) then
  199. begin
  200. //Modified by Dennies Chang.
  201. // Line := Line + QPHex(Buffer[i]);
  202. s := QPHex(Buffer[i]);
  203. Line := Line + s;
  204. end
  205. else
  206. Line := Line + Buffer[i];
  207. '=' : {Do not Localize}
  208. begin
  209. //Modified by Dennies Chang.
  210. //Line := Line + QPHex(Buffer[i]);
  211. s := QPHex(Buffer[i]);
  212. Line := Line + s;
  213. end
  214. else
  215. begin
  216. if ((Buffer[i] >= #33 ) and (Buffer[i] <= #60 )) or ((Buffer[i] >= #62) and (Buffer[i] <= #126 )) then
  217. begin
  218. Line := Line + Buffer[i];
  219. end
  220. else
  221. begin
  222. if Buffer[i] in [#10,#13] then
  223. begin
  224. Line := Line + Buffer[i]
  225. end
  226. else
  227. begin
  228. Line := Line + QPHex(Buffer[i]);
  229. end;
  230. end; //...else
  231. end; //..else
  232. end; //case buffer[i] of
  233. if Length(Line) > 71 then
  234. begin
  235. NewLine;
  236. end; //if Length(Line > 71 then
  237. end; //For i := 1 to LBufSize do
  238. end; //while LBytesRead < LDataSize do
  239. end; //if (LDataSize > 0) then {This ensures that the remaining is added to the TStrings}
  240. if Length(Line) >0 then
  241. begin
  242. st.Add(Line);
  243. end;
  244. Result := st.Text;
  245. //Delete an extra system EOL that was added by the TStrings itself
  246. //The EOL varies from system to system
  247. i := Length(sLineBreak);
  248. if (Length(Result)>i) then
  249. begin
  250. Delete(Result,Length(Result) - i+1,i);
  251. end;
  252. finally
  253. FreeAndNil(st);
  254. end;
  255. end;
  256. end.